從 C++ 到 Objective-C 的快速指南

英文原文:From C++ to Objective-C: A quick guide for practical programmers

標籤:  Objective-C  C/C++
233人收藏此文章, 我要收藏oschina 推薦於 2年前 (共 11 段, 翻譯完成於 05-13) (29評
 
參與翻譯(4人):

簡介

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

請注意這毫不是一個完整的指南,可是它讓你避免了閱讀100頁的手冊。除此以外,我知道你喜歡個人寫做風格。java

背景

須要C++的技能,我會比較C++和Objective-C的東西。此外,COM編程也是有用的,由於Objective-C有相似於IUnkown的東西,所以基礎的COM編程是有幫助的(但不是必須的)程序員

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

地獄星星
地獄星星
翻譯於 2年前

2人頂算法

 

 翻譯的不錯哦!編程

其它翻譯版本(2)

鐺 - 鐺!

咱們立刻就開始咱們的教程. 首先我會介紹 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
// call
[obj foo2 val1: 5 ];   // merely helper: You remember that 5 is assigned to param name val1.

 

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

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

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

?
1
2
3
4
5
6
7
8
9
// Objective-C
NSObject* ptr = ...;  // some pointer
[ptr foo: 5 : 3 ];  // call 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對象的檢查成爲可能.

leoxu
leoxu
翻譯於 2年前

0人頂

 

 翻譯的不錯哦!

協議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 

leoxu
leoxu
翻譯於 2年前

1人頂

 

 翻譯的不錯哦!

向下轉型

 

 

?
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;  // 或者編譯器會警告咱們說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的一個數字來建立一個選擇器, 這取決於能夠傳入多少參數. 選擇器實際上就是一個字符串,它會在運行時綁定到一個方法識別器.

leoxu
leoxu
翻譯於 2年前

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
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 call
}
@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,則對象會從內存中被移除掉.

leoxu
leoxu
翻譯於 2年前

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
@interface  f1 : NSObject
{
}
@property  (weak) NSAnotherObject* f2;  // 當沒有其它強引用存在的時候,它將會被自動設置成.

 

?
1
2
3
4
5
6
7
8
@end
 
- ( void ) foo
{
NSObject* s = [NSObject alloc] init];  // 若是構造失敗的話會返回nil
// use s
// end. Hooraah! Compiler will automatically call [s release] for us!
}

 

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

leoxu
leoxu
翻譯於 2年前

0人頂

 

 翻譯的不錯哦!

爲了能幫助編譯器決定保留一個對象多長時間,你還要一個弱引用指向一個變量. 默認的,全部的變量都是強引用(==只要強引用還存在,被引用的對象就會存在下去) . 你也能夠獲取一個弱引用,它會隨着每一個強引用消失而失去它的值. 這在類成員從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,

leoxu
leoxu
翻譯於 2年前

0人頂

 

 翻譯的不錯哦!

數組

?
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。

無若
無若
翻譯於 2年前

0人頂

 

 翻譯的不錯哦!

塊 和 Lambda

?
1
2
3
4
5
6
// Objective-C
// member function
-( void )addButtonWithTitle:(NSString*)title block:( void (^)(AlertView*, NSInteger))block;
 
// call
[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以前包含任何值(這很危險是否是?).

leoxu
leoxu
翻譯於 2年前

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*. 不然,你將會遇到嚴重的內存故障.

leoxu
leoxu
翻譯於 2年前

0人頂

 

 翻譯的不錯哦!

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 方法

  • 引用

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

閱讀更多
從C++ 到 Objective-C 指南, 這裏.

歷史記錄

  • 10/05/2014 : 第一次發佈.

相關文章
相關標籤/搜索