你好,歡迎來到IOS教程網

 Ios教程網 >> IOS使用技巧 >> IOS技巧綜合 >> 從 C++ 到 Objective

從 C++ 到 Objective

編輯:IOS技巧綜合
[摘要]本文是對從 C++ 到 Objective-C 的快速指南的講解,對學習IOS蘋果軟件開發有所幫助,與大家分享。

  簡介

  當我開始為iOS寫代碼的時候,我意識到,作為一個C++開發者,我必須花費更多的時間來弄清楚Objective-C中怪異的東西。這就是一個幫助C++專家的快速指南,能夠使他們快速的掌握Apple的iOS語言。

  請注意這絕不是一個完整的指南,但是它讓你避免了閱讀100頁的手冊。除此之外,我知道你喜歡我的寫作風格。

  背景

  需要C++的技能,我會比較C++和Objective-C的東西。此外,COM編程也是有用的,因為Objective-C有類似於IUnkown的東西,因此基礎的COM編程是有幫助的(但不是必須的)

  Objective C++是C++和Objective C的組合。你也能使用任何C++的東西混合到Objective C中,但是記住重新命名你的文件從.m到.mm

  铛 - 铛!

  我們馬上就開始我們的教程. 首先我會介紹 Objective-C 的東西,然後是C++中與它對等的東西.

  成員函數

1 2 3 4 5 6 7 8 9 10 11 12 // Objective-C - (int) foo : (int) a : (char) b {} + (int) foo : (int) a : (char) b {} // C++ int foo(int a,char b) {} static int foo(int a,char b) {} // Objective-C - (void) foo2 val1:(int) a; // named argument // <span id="22_nwp" ><a id="22_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=18&is_app=0&jk=b2645dbfe197c88d&k=call&k0=call&kdi0=0&luki=1&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=8dc897e1bf5d64b2&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F4400%2Ehtml&urlid=0" target="_blank" mpid="22" ><span >call</span></a></span> [obj foo2 val1:5]; // merely helper: You remember that 5 is assigned to param name val1.

  - 表示的是一個一般的成員函數(通過一個對象實體訪問), 而 + 則表示一個靜態成員函數, 不需要使用實體就能訪問. 當然,像C++, 靜態成員不能訪問實體變量.

  此外,Objective-C函數函數可以有賦予了名稱的參數,這樣讓什麼參數獲得什麼值會更一目了然. 理論上,被賦予了名稱的參數允許程序員按任何順序傳入參數,但是Objective-C卻規定要按聲明的順序傳參.

  通過一個指針或者一個靜態成員調用一個成員

1 2 3 4 5 6 7 8 9 // Objective-C NSObject* ptr = ...; // some pointer [ptr foo:5:3]; // <span id="19_nwp" ><a id="19_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=18&is_app=0&jk=b2645dbfe197c88d&k=call&k0=call&kdi0=0&luki=1&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=8dc897e1bf5d64b2&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F4400%2Ehtml&urlid=0" target="_blank" mpid="19" ><span >call</span></a></span> foo member with arguments 5 and 3 [NSObject staticfoo:5:3]; // call static function of NSOBject with arguments 4 and 3 // C++ CPPObject* ptr = ...; // some pointer ptr->foo(5,3); CPPObject::staticfoo(5,3);

  Objective-C 使用 [ ] 來調用成員函數並傳入用:分割開的參數, 其對於ObjectiveC中ptr為nil的情況非常友好,在這種情況下“調用”將會被忽略掉(而在C++中,這種情況會拋出一個指針沖突異常). 這使得消除對nil對象的檢查成為可能.

  協議VS接口

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // Objective-C @protocol foo - (void) somefunction; @end @interface c1 : NSObject<foo> @end @implementation c1 - (void) somefunction { ... } @end // C++ class foo { virtual void somefunction() = 0; }; class c1 : public NSObject, public foo { void somefunction() { ... } }

  協議= 抽象類. Objective-C 和 C++ 之間的區別在於,在 Objective-C 中,函數並不是必須要被實現的. 你可以讓一個可選的方法被動的被聲明,而這僅僅只是向編譯器發出暗示而已,並不是編譯必須的.

  檢查一個方法是否被實現了

1 2 3 4 5 6 7 // Objective-C NSObject* ptr = ...; // some pointer [ptr somefunction:5:3]; // NSObject 不必一定為其編譯而實現somefunction. 如果沒有被實現的話,會引發異常. // C++ CPPObject* ptr = ...; // some pointer ptr->somefunction(5,3); // CPPObject 必須實現 somefunction() 否則程序根本就不會被編譯.

  Objective-C 成員函數就是(Smalltalk中的) "消息" 而在Objective-C中時,我們則說接收者(即指針)會向一個選擇器做出回應, 這意味著其實現了我們嘗試去調用的虛函數. 當有一個接口是, C++ 對象必須實現其所有的成員函數. 而在 Objective-C 中這並不是必須的,因此我們可以向並不必須要實現它的某個地方發送一個”消息“(如此就會引發一個異常).

1 2 3 4 // Objective-C NSObject* ptr = ...; // some pointer if ([ptr respondsToSelector:@selector(somefunction::)] [ptr somefunction:5:3];

  現在我們就可以確定接收者向選擇器做出回應, 我們因此就可以調用它了. 在 C++ 中不需要這樣的檢查, 因為實現必須常常”向選擇器做出回應“, 否則源代碼根本就不會被編譯. 請注意我們必須知道選擇器獲取了多少個參數(因此在該@selector中是2個 ::s

  向下轉型

1 2 3 4 5 6 7 8 9 10 // Objective-C NSObject* ptr = ...; // some pointer if ([ptr isKindOfClass:[foo class]] [ptr somefunction:5:3]; // C++ CPPObject* ptr = ...; // some pointer foo* f = dynamic_cast<foo*>(ptr); if (f) f->somefunction(5,3);

  現在只有使用NSObject的"isKindOfClass"助手——所有Object-C類的基礎,才能像在C++中那樣向下轉型.

  符合協議?

1 2 3 4 5 6 7 8 9 // Objective-C NSObject* ptr = ...; // some pointer if ([ptr conformsToProtocol:@protocol(foo)] [ptr somefunction:5:3]; // C++ CPPObject* ptr = ...; // 某個也繼承自foo的指針 foo* f = ptr; // 或者<span id="15_nwp" ><a id="15_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=18&is_app=0&jk=b2645dbfe197c88d&k=%B1%E0%D2%EB%C6%F7&k0=%B1%E0%D2%EB%C6%F7&kdi0=0&luki=8&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=8dc897e1bf5d64b2&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F4400%2Ehtml&urlid=0" target="_blank" mpid="15" ><span >編譯器</span></a></span>會警告我們說ptr不能同foo兼容. f->somefunction(5,3);

  現在我們要檢查接收器是否符合一個協議(或者說,在C++就是實現一個接口), 以便我們可以發送這個協議包含的消息. 嘿嘿,它像極了Java的類和接口,而在C++中,完全被實現的類和一個“接口”之間沒有技術上的差別.

  void* 、 id 或者 SEL?

1 2 3 4 5 6 7 8 9 10 11 // Objective-C id ptr = ...; // some pointer if ([ptr conformsToProtocol:@protocol(foo)] [ptr somefunction:5:3]; SEL s = @selector(foo:); // a pointer to a function foo that takes 1 parameter // C++ void* ptr = ...; // some pointer foo* f = dynamic_cast<foo*>(ptr); if (f) f->somefunction(5,3);

  id 是通用的用於Objective-C類的類似於 void* 的東西. 你只能使用id而不是void*因為id可以通過ARC(稍後會詳細介紹到它)編程一個可被管理的指針,而因此編譯器需要在元指針類型和Objective-C指針之間做出區分.SEL 是一個用於選擇器(C++函數指針)的通用類型,而你一般可以通過使用關鍵字@selector帶上函數名字和:::::s的一個數字來創建一個選擇器, 這取決於可以傳入多少參數. 選擇器實際上就是一個字符串,它會在運行時綁定到一個方法識別器.

  類定義,方法,數據,繼承

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 // Objective C @class f2; // forward declaration @interface f1 : NSOBject // Objective-C supports only public and single inheritance { int test; // default = protected @public int a; int b; f2* f; } - (void) foo; @end @implementation f1 - (void) foo { a = 5; // ok self->a = 5; // ok super.foo(); // parent <span id="11_nwp" ><a id="11_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=18&is_app=0&jk=b2645dbfe197c88d&k=call&k0=call&kdi0=0&luki=1&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=8dc897e1bf5d64b2&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F4400%2Ehtml&urlid=0" target="_blank" mpid="11" ><span >call</span></a></span> } @end // C++ class f1 : public CPPObject { int test; // default = private public: class f2* f; // forward declaration int a; int b; void foo(); } void f1 :: foo() { a = 5; // ok this->a = 5; // ok CPPOBject::foo(); // parent call }

  Objective-C中的實現范圍在@implementation/@end 標記 (在C++ 中我們可以將實現放在任何帶有::范圍操作符的地方)之中. 它使用@class關鍵字用於事先聲明. Objective-C 默認帶有 私有(private)保護, 但僅用於數據成員(方法必須是公共的). Objective-C 使用self 而不是this,並且它還可以通過super關鍵字調用它的父類.

  構造器和析構器

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // Objective-C NSObject* s = [NSObject alloc] init]; // can return nil if construction failed [s retain]; // Increment the ref count // C++ CPPObject* ptr = new CPPObject(); // can throw ptr->AddRef(); // Objective-C NSObject* s = [NSObject alloc] initwitharg:4]; [s release]; // C++ CPPOBject* ptr = new CPPOBject(4); ptr->Release();

  Objective-C中的內存分配是通過靜態成員方法alloc來實現的,所有(做為NSObject後裔)的對象都有這個方法.self 在Objective-C中是可以被賦值的,而如果構建失敗的話它就會設置成nil(而在C++中則會被拋出一個異常). 內存分配之後實際被構造器調用的是一個公共的成員函數,在Objective-C中默認的是init方法.

  Objective-C 使用同COM益陽的引用計數方法, 而它也使用retain 和release(等同於IUnknown的AddRef() 和 Release() 方法). 當引用計數到了0,則對象會從內存中被移除掉.

  多線程

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // Objective C @interface f1 : NSOBject // Objective-C supports only public and single inheritance { } - (void) foo; - (void) threadfunc :(NSInteger*) param; - (void) mt; @end @implementation f1 - (void) threadfunc : (NSInteger*) param { [self performSelectorOnMainThread: @selector(mt)]; } - (void) mt { } - (void) foo { [self performSelectorInBackground: @selector(thradfunc:) withObject:1 waitUntilDone:false]; <div></div>} @end

  Objective-C 有一些針對NSObject的內建功能,可以在另外一個線程中操作一個選擇器 (== 調用一個成員), 在主線程中,等待一次調用等等 . 在NSObject參見更多信息.

  內存和ARC

1 2 3 4 5 //Objective-C @interfacef1:NSObject { } @property(weak)NSAnotherObject*f2;//當沒有其它強引用存在的時候,它將會被自動設置成. 1 2 3 4 5 6 7 8 @end -(void)foo { NSObject*s = [NSObject alloc]&nbsp;init]; // 如果構造失敗的話會返回nil // use s // end. Hooraah! Compiler will automati<span id="9_nwp" ><a id="9_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=18&is_app=0&jk=b2645dbfe197c88d&k=call&k0=call&kdi0=0&luki=1&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=8dc897e1bf5d64b2&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F4400%2Ehtml&urlid=0" target="_blank" mpid="9" ><span >call</span></a></span>y call[s release] for us! }

  這裡需要你忘記自己良好的C++ 習慣. OK Objective-C 使用了一個垃圾收集機制,這種東西我們C++很討厭,因為它很慢並會讓我們想到Java. 但是 ARC (自動進行引用計算) 是一種編譯時間特性,它會告訴編譯器 "這裡是我的對象:請幫我算算啥時候它們才要被銷毀吧". 使用ARC你就不需要發送retain/release消息給你的對象了; 編譯器會自動幫你代勞的.

  為了能幫助編譯器決定保留一個對象多長時間,你還要一個弱引用指向一個變量. 默認的,所有的變量都是強引用(==只要強引用還存在,被引用的對象就會存在下去) . 你也可以獲取一個弱引用,它會隨著每個強引用消失而失去它的值. 這在類成員從XCode的Builder Interface(像RC 編輯器)處獲取它們的值時,會很有用,當類被銷毀時,那些成員也會失去它們的值.

  Strings

1 2 3 4 5 // Objective-C NSString* s1 = @"hello"; NSString* s2 = [NSString stringWithUTF8String:"A C String"]; sprintf(buff,"%s hello to %@","there",s2); const char* s3 = [s2 UTF8String];

  NSString是一個Objective-C字符串的不可變表示. 你可以使用其一個靜態方法,或者是一個帶有@前綴的字符串文本來創建NSString. 你也可以使用 %@ 來向printf家族函數來表示一個NSString,

  數組

1 2 3 // Objective-C NSArray* a1 = [NSArray alloc] initWithObjects: @"hello",@"there",nil]; NSString* first = [a1 objectAtIndex:0];

  NSArray和NSMutableArray是在Objective-C中處理數組的 兩個類(兩者的差異是,NSArray元素構造時必須通過構造函數,而NSMutableArray可以在之後更改)。典型構造函數的生效,你必須通過nil去作為“結尾元素”。排序/搜索/插入函數對於NSArray和NSMutableArray來說是一樣的,在第一行中的例子它返回一個新的NSArray,而在NSMutableArray的例子裡,它修改的是一個存在的對象。

  分類

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // C++ class MyString : public string { public: void printmystring() { printf("%s",c_str()); } }; // Objective-C @interface MyString (NSString) - (void) printmystring; @end @implementation MyString (NSString) - (void) printmystring { printf("%@",self); } @end // C++ MyString s1 = "hi"; s1.printmystring(); // ok string s2 = "hello"; s2.printmystring(); // error, we must change s2 from string to MyString // Objective-C NSString* s2 = @"hello"; [s2 printmystring]; // valid. We extended NSString without changing types.

  C++依賴繼承機制來實現一個已知的類。這是很麻煩的,因為所有用戶的實現類必須使用另外的類型名稱(在例子中,MyString用來代替string)。Object-C通過使用分類(Categories)允許擴展一個已知的類內同型(same type )。上面鏈接中所有源代碼在extension.h文件 (具有代表性的是像NSString+MyString.h這樣的)中可以查看,上面例子中,我們立即就有可以調用新的成員函數,而不需要改變NSString類型為MyString。

  塊 和 Lambda

1 2 3 4 5 6 // Objective-C // member function -(void)addButtonWithTitle:(NSString*)title <span id="4_nwp" style="width: auto; height: auto; float: none;"><a id="4_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=18&is_app=0&jk=b2645dbfe197c88d&k=block&k0=block&kdi0=0&luki=3&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=8dc897e1bf5d64b2&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F4400%2Ehtml&urlid=0" target="_blank" mpid="4" style="text-decoration: none;"><span style="color:#0000ff;font-size:14px;width:auto;height:auto;float:none;">block</span></a></span>:(void(^)(AlertView*, NSInteger))block; // <span id="5_nwp" ><a id="5_nwl" href="http://cpro.baidu.com/cpro/ui/uijs.php?adclass=0&app_id=0&c=news&cf=1001&ch=0&di=128&fv=18&is_app=0&jk=b2645dbfe197c88d&k=call&k0=call&kdi0=0&luki=1&n=10&p=baidu&q=06011078_cpr&rb=0&rs=1&seller_id=1&sid=8dc897e1bf5d64b2&ssp2=1&stid=0&t=tpclicked3_hc&tu=u1922429&u=http%3A%2F%2Fwww%2Eadmin10000%2Ecom%2Fdocument%2F4400%2Ehtml&urlid=0" target="_blank" mpid="5" ><span >call</span></a></span> [object addButtonWithTitle:@"hello" block:[^(AlertView* a, NSInteger i){/*DO SOMETHING*/}];

  塊是Objective-C 用來模擬lambda功能的一種方式. 查看Apple的文檔,從AlertView的示例(使用塊的UIAlertView)可以獲得更多有關塊的技術.

  C++ 開發者使用Objective-C 和 ARC 的重要提示

1 2 3 4 5 6 7 8 9 10 11 12 13 // C++ class A { public: NSObject* s; A(); }; A :: A() { s = 0; // 可能會奔潰,這是常發生在發布模式下! }

  你已經知道給所有的顧客都打兩折對你而言有多痛苦了,因為你bug重重的軟件會在發布模式下奔潰,而在調試模式下總是妥妥的. 沒有用戶會理解程序員,是不是?

  讓我們來看看這裡發生了什麼. s = 0這一行將 0 賦值給了一個變量,而因此不管這個變量之前取值是什麼,首先都會被釋放掉,所以編譯器在賦值之前執行了一次 [s release] . 如果 s 之前已經是 0 了,假設是在調試構建的話,不會發生任何不好的事情; 如果 s 是一個nil的話,使用[s release] 是再好也不過的. 然而,在發布模式下, s可能是一個野指針,所以它可能在被“初始化”為0之前包含任何值(這很危險是不是?).

  在C++中,這並不是一個問題,因為它裡面是不存在ARC的. 而在Objective-C中編譯器並沒有辦法了解這是一次"賦值" 還是一次 "初始化" (如果是後者,它就不會發送發布消息).

  下面是正確的方式:

1 2 3 4 5 6 7 8 9 10 11 12 // C++ class A { public: NSObject* s; A(); }; A :: A() :s(0) // 現在編譯器就知道確定 it&apos;s 是一次初始化了, 一次就不存在 [s release] { }

  現在編譯器就不會去嘗試調用一個 [s release] 因為它知道它是這個對象的第一次初始化. 請小心!

  從Objective-C 對象到 C++ 類型的轉換

1 2 3 4 5 // Objective-C NSObject* a = ...; void* b = (__bridge void*)a; // 你必須在Objective-C和C類型支架使用 __bridge void* c = (__bridge_retained void*)a; // 現在是一個+1的保留計數,而你必須在稍後釋放這個對象 NSObject* d = (__bridge_transfer NSObject*)c; // 現在ARC取得了對象c的”擁有權“, 將其裝換成一個ARC管理的NSObject.

  我可以分析這一切,而我的建議是簡單的.不要將ARC類型和非ARC類型混在一起. 如果你必須轉換一些Objective-C對象的話,使用id而不是void*.否則,你將會遇到嚴重的內存故障.

  Objective-C 有而 C++ 沒有的

    分類Categories

    基於NSObject的操作

    YES 和 NO (等價於true和false)

    NIL 和 nil (等價於0)

    可命名的函數參數

    self (等價於this) 而其可以在一個構造器中被改變

 C++ 有而Objective-C 沒有的

    靜態對象. Objective-C 中的對象不能被初始化成靜態的,或者是存在於棧中. 只能是指針.

    多重繼承

    命名空間

    模板

    操作符重載

    STL 和算法 ;

    方法可以是受保護的( protected )或者私有的( private ) (在Obj-C中,只能是公共的)

    const/mutable 項

    friend 方法

    引用

    匿名函數簽名 (沒有變量名稱)

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved