第 2 章 对象、消息、运行时

0.用 Objective-C 等面向对象语言编程时,“对象”就是“基本构造单元”(building block),开发者可以通过对象来存储并传递数据。在对象之间传递数据并执行任务的过程就叫做“消息传递”(Messaging)。

1.当应用程序运行起来以后,为其提供相关支持的代码叫做“Objective-C 运行期环境”(Objective-C Runtime),它提供了一些使得对象之间能够提供消息的重要函数,并且包含创建类实例所用的全部逻辑。在理解了运行期环境中各个部分协同工作的原理之后,你的开发水平将会进一步提升。

2.属性(property)是 Objective-C 的一项特性,用于封装对象中的数据。Objective-C对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中“获取方法”(getter)用于读取变量值,而“设置方法”(setter)用于写入变量值。这个概念已经定型,并且经由“属性”这一特性而成为 Objective-C 2.0 的一部分,开发者可以令编译器自动编写与属性相关的存取方法。此特性引入了一种新的“点语法”(dot syntax),使开发者可以更为容易地依照类对象来访问存放于其中的数据。

3.要访问属性,可以使用“点语法“,在纯 C 中,如果想访问分配在栈上的 struct 结构体里面的成员,也需使用类似语法。编译器会吧”点语法“转换为对存取方法的调用,使用”点语法“的效果与直接调用存取方法相同。因此,使用”点语法“和直接调用存取方法之间没有丝毫差别。

4.属性还有更多优势。如果使用了属性的话,那么编译器就会自动编写访问这些属性所需的方法,此过程叫做”自动合成“。需要强调的是,这个过程由编译器在编译期执行,所以编辑器里看不到这些”合成方法“的源代码。除了生成方法代码之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加上下划线,以此作为实例变量的名字。也可以在类的实现代码里通过 @synthesize 语法来指定实例变量的名字。

5.一般情况下无须修改默认的实例变量名,但是如果你不喜欢以下划线来命名实例变量,那么可以用这个办法将其改为自己想要的名字。笔者还是推荐使用默认的命名方案,因为如果所有人都坚持这套方案,那么写出来的代码大家都能看得懂。

6.若不想令编译器自动合成存取方法,则可以自己实现。如果你只实现了其中一个存取方法,那么另外一个还是会由编译器来合成。还有一种办法能阻止编译器自动合成存取方法,就是使用 @dynamic 关键字,它会告诉编译器:不要自动创建实现属性所用的实例变量,也不要为其创建存取方法。而且,在编译访问属性的代码时,即使编译器发现没有定义存取方法,也不会报错,它相信这些方法能在运行期找到。

7.使用属性时还有一个问题要注意,就是其各种特质(attribute)设定也会影响编译器所生成的存取方法。属性可以拥有的特质分为 4 类:

  • 原子性
  • 读/写权限
  • 内存管理语义
  • 方法名

8.在属性不添加任何特质时,MRC 默认情况是 atomicassignreadwrite。ARC 默认情况是 atomicstrongreadwrite

@property BOOL checked;

http://stackoverflow.com/questions/5802511/what-are-the-defaults-values-for-property-in-ios
http://www.devtalking.com/articles/you-should-to-know-property/

9.原子性:在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备 nonatomic 特质,则不使用同步锁。

10.具备 readwrite(读写)特质的属性拥有“获取方法”(getter)与“设置方法”(setter)。

11.具备 readonly(只读)特质的属性仅拥有获取方法。你可以使用此特质把某个属性对外公开为只读属性,然后在“class-continuation 分类”中将其重新定义为读写属性。

12.assign 设置方法 只会执行针对 “纯量类型”(scalar type,例如 CGFloatNSInteger 等)的简单赋值操作。

13.strong 此特质表明该属性定义了一种“拥有关系”(owning relationship)。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。

14.weak 此特质表明该属性定义了一种“非拥有关系”(nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同 assign 类似,然而在属性所指的对象遭到销毁时,属性值也会清空(nil out)。

15.unsafe_unretained 此特质的语义和 assign 相同,但是它适用于 对象类型,该特质表达一种 “非拥有关系”(“不保留”,unretained),当目标对象遭到销毁时,属性值不会自动清空(“不安全”,unsafe),这一点与 weak 有区别。

16.copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其拷贝(copy)。

17.当属性类型为 NSString* 时,经常用此特质来保护其封装性。因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可以修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变”(immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的”(mutable),就应该在设置新属性值时拷贝一份。NSDictionaryNSArray 也应如此。

18.可通过如下特质来制定存取方法的方法名:

@property(nonatomic, getter=isOn) BOOL on;

19.setter= 指定“设置方法”的方法名。

20.通过上述特质,可以微调由编译器所合成的存取方法。不过需要注意:若是自己来实现这些存取方法,那么应该保证其具备相关属性所声明的特质。比方说,如果将某个属性声明为 copy,那么就应该在”设置方法“中拷贝相关对象,否则会误导该属性的使用者,而且,若是不遵从这一约定,还会令程序产生 bug。

21.由于是只读属性,所以编译器不会为其创建对应的“设置方法”,即便如此,我们还是要写上这些属性的语义,以此表明初始化方法在设置这些属性值时所用的方式。要是不写明语义的话。该类的调用者就不知道初始化方法里会拷贝这些属性,他们有可能会在调用初始化方法之前自行拷贝属性值。这种操作是多余而且低效的。

22.atomicnonatomic 的区别

具备 atomic 特质的获取方法会通过锁定机制来保证其原子性。这也就是说,如果两个线程读写同一属性,那么不论何时,总能看到有效的属性值。若是不加锁的话(或者说使用 nonatomic 语义),那么当其中一个线程正在改写某属性值,另一个线程也许会突然闯入,把尚未修改好的属性值读取出来。发生这种情况时,线程读到的属性值可能不对。

23.atomic 一定是线程安全的吗?

如果开发过 iOS 程序,你就会发现,其中所有属性都声明为 nonatomic。这样做的历史原因是:在 iOS 中使用同步锁的开销较大,这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(thread safety),若要实现“线程安全 ”的操作,还需采用更为深层的锁定机制才行。例如,一个线程在连续多次读取某属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为 atomic,也还是会读到不同的属性值。因此,开发 iOS 程序时一般都会使用 nonatomic 属性。但是在开发Mac OS X 程序时,使用 atomic 属性通常都不会有性能瓶颈。

24.消息转发

http://www.jianshu.com/p/1bde36ad9938

25.Runtime(貌似满大街都是的文章了)

http://www.jianshu.com/p/e071206103a4