从 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 : 第一次发布.

相关文章
相关标签/搜索