第7条,在对象内部尽量直接访问实例变量
- 直接访问实例变量的方法是:下划线+变量名;通常访问方式:self点语法或者类对象点语法。
- 直接访问实例变量,速度会更快(利)。
- 直接访问实例变量,不会调用set方法,绕过了属性的内存管理(利)。
- 直接访问实例变量,不会触发KVO键值观察(利)。
- 属性访问,有助于排查错误,可以在set和get中断点监控属性的调用和访问(弊)。
- 合理使用方式:写入实例变量的时候用属性访问,读取实例变量的时候直接访问。
- 合理使用情况注意事项:有子类情况下,父类与子类共用变量,在初始化赋值的时候用直接访问方式赋值防止覆写;如果有懒加载式的初始化时那么在获取实例变量的值的时候用属性访问方式,即get来获取,直接访问的方式可能获取nil值(有点类似线程安全)。
关键词:直接访问实例变量、属性访问实例变量
第8条,理解“对象等同性”这一概念
- “==”操作符比较的结果是指针比较,不是对象比较,它是有局限性的。
- “isEqual”可以用来判断两个对象的等同性。
- 特定类型的对象(NSString、NSArray、NSDictionary),用特定的等同性判断(isEqualToString、isEqualToArray、isEqualToDictionary)比单纯的“isEqual”速度要快。
- 相同对象有相同的哈希码,有相同哈希码的对象不一定相同。
- hash方法检测的时候有性能问题,需要使用计算速度快哈希码碰撞几率低的算法。
关键词:等同性、isEqual、hash码
第9条,以“类族模式”隐藏实现细节
- 相似的类可以用父子类的形式重构,但是开发人员需要记住很多类名,为了简化,引出“类族模式”概念。
- 类族就是在一个基础类中统一通过枚举的方式创建各种分类,然后在实体子类里通过继承类族基类实现各自功能。
- 系统框架里有很多类族,防止和类族进行等同性比较。
- 判断实例所属的类是否在类族中,不要用等同性比较,用isKindOfClass。
- 在类族中新增子类时需要遵守规则:子类应该继承自类族中的抽象基类;子类应该定义自己的数据存储方式;子类应该覆写超类文档中指明需要覆写的方法。
关键词:类族模式、isKindOfClass
第10条,在既有类中使用关联对象存放自定义数据
- 关联对象就是多个对象通过共同的key在不同的位置处理关联的问题([object setObject:value forKey:key] / [object objectForKey:key])。
- 可以通过“关联对象”机制把两个对象连接起来。
- 定义关联对象时,可以指定内存管理语义达到模仿属性的拥有/非拥有关系。
- 有其他方式的时候,尽量不用关联对象,容易引起未知bug。
关键词:关联对象、KVC、
第11条,理解 objc_msgSend 的作用(重点)
- objc_msgSend是OC和C语言间的传话筒。
- OC的方法调用需要像消息一样传给编译器,objc_msgSend就是传递中介。
- OC的方法调用没运行之前只是一个函数调用指令,不能确定具体硬编码,只有运行期才会读取编译相应指令,在这之前都是动态的(动态绑定)。
- objc_msgSend本身是参数可变的函数,第一个参数表示接收者,第二个参数表示选择子(方法名),第三个及以后的参数表示消息中携带的参数。
- objc_msgSend的工作原理:根据接受者所属于的类,在这个类中方法列表中找选择子相同的方法名,要是找到对应的方法就跳转到实现代码;要是没找到,就沿着这个类的继承链往父类中继续找;要是最终还没找到就执行“消息转发”。
- 为了避免每次相同的消息都按照工作原理的方式进行处理,影响性能,在找到对应方法后,添加一个缓存映射表,在下次遇到相同消息的时候直接执行缓存逻辑。
- 边界消息调用情况:objc_msgSend_stret(待发送的消息要返回结构体)、objc_msgSend_fpret(消息返回浮点数)、objc_msgSendSuper(给超类发消息)。
关键词:objc_msgSend、消息传递、动态绑定、接收者、 选择子
第12条,理解消息转发机制(重点)
- 编译期向类发送无法解读的消息不会报错,因为运行期还有机会继续向类中添加方法。
- 编译器在编译时无法确知类中是否有某个方法的实现。
- 当对象接收到无法解读的消息的时候,就会启动“消息转发”机制。
- 消息转发机制的工作原理: 消息转发分两个大阶段。第一阶段先征询接受者所属的类,看能否动态添加方法,用来处理“未知选择子”(我称之为:暂时无人要的函数),这个过程简称“动态方法解析”。第二阶段涉及“完整消息转发机制”,运行期系统把第一阶段执行完后,这个时候的接受者不能再以动态新增方法去响应“未知选择子”,所以又继续分两步走:第1步,请接受者看看有没有其他对象能处理这个消息,若有则把消息转发给那个对象,整个消息转发结束,若没有进入第2步;第2步,若没有“备援接收者”,则启动完整消息转发机制,运行期系统把与消息有关的全部细节都封装到NSInvocation对象中,再给接受者最后的一次机会,令其设法解决这个还未处理的消息。
- 备援接收者:接受者第二次处理“未知选择子”的替代接收者,在继承关系中一般会往父类中查找。
- 完整消息转发机制:首先创建NSInvocation对象,把与尚未处理的那条消息有关的全部细节都封于其中,此对象包含选择子、目标(target)及参数。在出发NSInvocation对象时,“消息派发系统”(message-dispatch system)将亲自出马,把消息指派给目标对象。
- 整个消息转发机制图示(摘自书本p49):
+ (BOOL)resolveInstanceMethod:(SEL)sel
- (id)forwardingTargetForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation
关键词:消息转发机制、动态方法解析、备援接收者、完整消息转发机制