Item 1 习惯Objective-c(译自Effective Objective-c)
Objective-c 和其他面向对象的语言如C++、Java类似,但也存在很多不同的地方。如果你以前用过其他的面向对象的语言,使用Oc时你也可以很容易理解很多类似的思想。当然OC的语法看起来可能有点奇怪,因为它使用消息传递
(messaging) 结构而不是调用函数的形式。消息传递和调用函数看起来就像:
//OC
Object *obj = [Object new];
[obj performWith:parameter1 and: parameter2];
//C++
Object *obj = new Object;
obj->perform(parameter1, parameter2);
最关键的不同就是messaging
的结构—在运行时决定执行哪部分代码。 调用函数的方法由编译器来决定执行哪部分代码。当函数调用的例子中引入多态时,运行时才通过查找虚表
来决定执行那个函数。但是messaging
总是在运行时查找。事实上,编译器根本就不关心,它传递的消息对象是什么类型的。通过称为动态绑定的方式来在运行时查找。
oc runtime
的环境component
而不是编译器做这些繁琐的工作,运行时的环境包含所有的数据结构和使得oc面向对象特性得以体现的函数。例如,runtime
包含所有的内存管理方法,本质上,runtime
时一个代码集,联系你所有的代码形成一个你的代码链接到的动态库。因此,只要runtime
更新了,你的应用就可以获得性能上的提升。而一种在编译时做更多的事的语言需要重新编译才能获得性能上的提升。
oc是c的超集,所以所有C语言支持的特性都可以在oc中使用。因此,如果你想编写高效的oc程序,你需要理解c和oc的核心概念。特别的,理解c的内存模型会帮助你理解oc的内存模型以及引用计数工作的方式,这也涉及到理解在oc中一个指针怎么表示一个对象。当你声明一个指向一个对象应用到变量时,语法如下:
NSString *something = @"The string"
这种语法很大程度和C一样,声明一个变量名为someString类型为NSString*
,也就是说这是一个指向NSString
的指针。所有的oc对象都是这样声明的因为对象的内存总是在堆中分配的不会在栈中分配。声明一个在栈中的oc对象是不合法的:
NSString stackString;
//error:interface type cannnot be statically allocated
变量someString
指向一个在栈中分配内存的一个NSString
对象。也就是说创造其他指向同一个地址的变量,不会复制一个对象,而是产生两个指向同一个对象的变量。
NSString *someString = @"The string";
NSString *anotherString = someString;
这里只有一个NSString
实例,但是有俩个变量指向同一个实例。这俩个变量的类型都是NSString*
, 也就是说当前的栈帧分配了一个指针结构2比特大小的内存(4比特,32位结构,8比特,64位结构)。这些大小比特的内存包含同样的值:一个NSString
实例的地址。
图1.1 描述了这种关系,NSString
实例存储的data
包含需要展示真实string的bytes。
在堆中分配的内存需要被直接管理,然而当栈帧被弹出时在栈帧上分配的保存变量的内存被自动清除。
堆内存管理在oc中被抽象了。不需要使用malloc
和free
来分配和撤销对象的内存。ocruntime
通过引用计数这种内存管理结构来把这个抽象出来了。
有时在oc中,你会遇到一些变量在定义时没有*
,可能会用到栈空间。这些变量没有保存oc对象,一个典型的例子是CoreGraphics
框架中的CGRect
:
CGRect frame;
frame.origin.x = 0.0f;
frame.origin.y = 10.0f;
frame.size.width = 100.0f;
frame.size.height = 150.0f;
一个CGRect
是一个C结构体,定义是:
struct CGRect{
CGPoint origin;
CGSize size;
}
typedef struct CGRect CGRect;
这些结构的类型直接通过系统框架来使用,大量的使用oc对象可能会影响程序性能。创建对象会导致像分配和回收堆内存的其他的开销,而使用结构体不会。当非对象类型(int,float, double,char等)是唯一需要保存的数据,像CGRect
这种结构体经常被用到。
在着手写OC代码之前,建议仔细了解下C语言,如果你直接使用oc,你可能会发现oc语法的某些部分很难理解。
总结
- oc是c的超集,增加了面向对象的特性。oc使用动态绑定这种消息传递结构,对象的类型在运行时决定。
runtime
而不是编译器根据消息来决定执行哪部分代码。 - 理解c的核心概念可以帮助你编写高效oc代码。特别地,你需要好好理解内存模型和指针的概念。