从 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人):

leoxu无若nbafifa02地狱星星

仅中文 | 中英文对照 | 仅英文 | 打印此文章

简介

当我开始为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

地狱星星
翻译于 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++, 静态成员不能访问实体变量.

此外,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
翻译于 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
翻译于 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
翻译于 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
翻译于 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
翻译于 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
翻译于 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
翻译于 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
翻译于 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 : 第一次发布.

时间: 2024-10-22 07:08:04

从 C++ 到 Objective-C 的快速指南的相关文章

Emacs 快速指南 - 原生中文手册

Emacs 快速指南 -折叠目录 1. 小结(SUMMARY) 2. 基本的光标控制(BASIC CURSOR CONTROL) 3. 如果 EMACS 失去响应(IF EMACS STOPS RESPONDING) 4. 被禁用的命令(DISABLED COMMANDS) 5. 窗格(WINDOWS) 6. 插入与删除(INSERTING AND DELETING) 7. 撤销(UNDO) 8. 文件(FILE) 9. 缓冲区(BUFFER) 10. 命令集扩展(EXTENDING THE C

转:C++ Boost/tr1 Regex(正则表达式)快速指南

C++ Boost/tr1 Regex(正则表达式)快速指南 正则表达式自Boost 1.18推出,目前已经成为C++11(tr1)的标准部分. 本文以Boost 1.39正则表达式为基础,应该广泛适用于其他版本的Boost.对namespace稍加修改,即可适用tr1标准. 0.regex对象 类似于Java中的Pattern,Boost中的正则表达式对象为: boost::regex 常见构造方法2种: 1 2 3 4 5 // 1. 直接使用正则表达式的字符串构造. boost::rege

(译)快速指南:用UIViewPropertyAnimator做动画

翻译自:QUICK GUIDE: ANIMATIONS WITH UIVIEWPROPERTYANIMATOR 译者:Haley_Wong iOS 10 带来了一大票有意思的新特性,像 UIViewPropertyAnimator,它是一个改善动画处理的全新的类. 这个视图属性动画完全颠覆了我们已经习惯的流程,能够为动画逻辑添加更精细的控制. 一个简单的动画 让我们来看看如何通过一个简单的动画改变视图的中心点属性. let animator = UIViewPropertyAnimator(du

Shell 快速指南

目录 Shell 快速指南??概述????什么是 shell????什么是 shell 脚本????Shell 环境??????指定脚本解释器????模式??????交互模式??????非交互模式??Shell 编程????解释器????注释????变量??????局部变量??????环境变量??????位置参数????Shell扩展??????大括号扩展??????命令置换??????算数扩展??????单引号和双引号????数组??????创建数组??????获取数组元素??????获取数组

【SFA官方翻译】使用 Kubernetes、Spring Boot 2.0 和 Docker 的微服务快速指南

[SFA官方翻译]使用 Kubernetes.Spring Boot 2.0 和 Docker 的微服务快速指南 原创: Darren Luo SpringForAll社区 今天 原文链接:https://dzone.com/articles/quick-guide-to-microservices-with-kubernetes-sprin 作者:Piotr Mińkowski 译者:Darren Luo 在本教程中你将学习如何使用 Kubernetes 和 Docker 快速启动并运行 Sp

Emacs 快速指南(中文翻译)

Emacs 快速指南 目录 1. 小结(SUMMARY) 2. 基本的光标控制(BASIC CURSOR CONTROL) 3. 如果 EMACS 失去响应(IF EMACS STOPS RESPONDING) 4. 被禁用的命令(DISABLED COMMANDS) 5. 窗格(WINDOWS) 6. 插入与删除(INSERTING AND DELETING) 7. 撤销(UNDO) 8. 文件(FILE) 9. 缓冲区(BUFFER) 10. 命令集扩展(EXTENDING THE COMM

有关如何修复Android手机上的SSL连接错误的快速指南

"您的连接不是私人的",对于这个问题相信不少朋友在Android设备上都出现过,其实这个问题是Android设备最常见的问题之一,这可能会让您感到困惑,因为它甚至出现在最新的操作系统和最新的设备上,但这不是什么大问题,但是还是让我们来看看如何修复Android手机上的SSL连接错误吧. 让我们从SSL/TLS证书开始吧 首先是一点背景知识.正如你可能知道的,SSL证书旨在对你即将进行访问的网站进行身份验证——过去这是由证书机构进行的,证书机构能够签发证书,确保你知道谁在你的连接的另一端

Haproxy 快速指南

Haproxy 快速指南快速开始配置 haproxy安装 haproxy yum -y install haproxy 配置 haproxy vi /etc/haproxy/haproxy.cfg global log 127.0.0.1 local0 chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 10000 user haproxy group haproxy daemon ulimit-n 100000 stats

Windows上安装配置RabbitMQ快速指南

安装Rabbit MQ Rabbit MQ 是建立在强大的Erlang OTP平台上,因此安装Rabbit MQ的前提是安装Erlang.通过下面两个连接下载安装3.2.3 版本: 下载并安装 Eralng OTP For Windows (vR16B03) 运行安装 Rabbit MQ Server Windows Installer (v3.2.3) 默认安装的Rabbit MQ 监听端口是5672 激活Rabbit MQ's Management Plugin 使用Rabbit MQ 管理