第2章 对象、消息、运行期
第6条:理解属性这一概念
可以使用@property语法来定义对象中所封装的数据。
通过“特质”来指定存取数据所需的正确语句。
在设置属性所对应的实例变量时,一定要遵从该属性所声明的语义。
开发iOS程序是应尽量使用nonatomic属性,因为atomic属性会严重影响性能。
@synthesize语法指定实例变量的名字。@synthesize firstName = _myFirstName;
@dynamic:它会告诉编译器:不要自动创建实例变量和存储方法。
assign:简单赋值操作。
strong:设置方法先保留新值,并释放旧值,然后将新值设置上去。
weak:设置方法既不保留新值,也不释放旧值。摧毁时,属性值清空(nil out);
unsafa_unretained:与assign相同,但摧毁时,属性值不会自动清空,与weak有区别。
copy:设置方法不保留新值,而是将其拷贝。
第7条:在对象内部尽量直接访问实例变量
由于不经过OC的“方法派发”步骤,所以直接访问实例变量的速度较快。在这种情况下,编译器所生成的代码会直接访问保存对象实例变量的那块内存。
直接访问实例变量时,不会调用其“设置方法”,这就绕过了为其属性所定义的“内存管理语义”。比方说,如果在ARC下直接访问一个声明为copy的属性,那么并不会拷贝该属性,只会保留新值,并释放旧值。
如果直接访问实例变量,那么不会触发“键值观测”(KVO)通知。这样做是否会产生问题,还取决于具体的对象行为。
通过属性来访问有助于排查与之相关的错误。
在对象内部读取数据时,应该直接通过实例变量来读,而在写入数据时,则应通过属性来写。
在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。
有时会使用懒加载,这种情况下,需要通过属性来读取数据。
第8条:理解“对象等同性”这一概念
若想检测对象的等同性,请提供“isEqual:”与hash方法。
相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同。
不要盲目地逐个检测每条属性,而是应该依照具体需求来制定检测方案。
编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。
第9条:以“类族模式”隐藏实现细节
类族模式可以把实现细节隐藏在一套简单的公共接口后面。
系统框架中经常使用类族。
从类族的公共抽象基类中继承子类时要小心,若有开发文档,首先阅读。
第10条:在既有类中使用关联对象存放自定义数据
可以通过“关联对象”机制来把两个对象连起来。
定义关联对象时可指定内存管理语义,用以模仿定义属性时所采用的“拥有关系”与“非拥有关系”。
只有在其他做法不可行时才应选择关联对象,因为这种做法通常会引入难以查找的bug。
第11条:理解objc_msgSend的作用
消息由接受者、选择子及参数构成。给某对象“发送消息(invoke a message)”也就是相当于在该对象上“调用方法”(call a method)。
发给某对象的全部消息都要由“动态消息派发系统”(dynamic message dispatch sysytem)来处理,该系统会查出对应的方法,并执行其代码。
第12条:理解消息转发机制
若对象无法响应某个选择子,则进入消息转发流程。
通过运行期的动态方法解析功能,我们可以在需要用到某个方法时再将其加入类中。
对象可以把其无法解读的某些选择子转交给其他对象处理。
经过上述两步之后,如果还是没有办法处理选择子,那就启动完整的消息转发机制。
第13条:用“方法调配技术”调试“黑盒方法”
在运行期,可以向类中新增或替换选择子所对应的方法实现。
使用另一份实现来替换原有的方法实现,这道工序叫做“方法调配”,开发者常用此技术向原有实现中添加新功能。
一般来说,只有调试程序的时候才需要在运行期修改方法实现,这种方法不易滥用。
第14条:理解“类对象”的用意
每个实例都有一个指向 Class 对象的指针,用以表明其类型,而这些 Class 对象则构成了类的继承体系。
如果对象类型无法再编译期确定,那么就应该使用类型信息查询(is kind of class)来探知。
尽量使用类型信息查询方法来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发功能。