对象,消息,运行期
理解“属性”这一概念
OC是通过运行时机制来提供相关支持的,属性则是用来封装OC对象中数据的类型,可以自动生成setter和getter方法,与其他非运行时语言所不同的是,类似于Java的语言在编译期就已经确定了成员变量在内存中的作用域,通过偏移量(该变量距离起始位置的距离)来查找该变量,而添加新的成员变量会导致异常,指向的变量出现错乱。
OC对这种情况的应对方法是:将实例变量当作一种存储偏移量的特殊变量交给类对象来管理,偏移量会在运行期查找,如果类进行变动,那么存储的偏移量也会变动。
对象内部尽量访问实例变量
作者建议在对象内部访问成员变量时,读取数据建议直接访问成员变量,而设置数据的时候,使用内部的setter方法。
- 因为访问getter方法时会经过OC的消息派发中心,而直接访问成员变量则会直接在内存中找到该区域,相应较快
- 设置数据时直接访问成员变量会绕过内存管理语义,例如copy的变量不会拷贝该属性,造成内存管理的异常
- 直接访问实例变量不会触发KVO,有可能会有问题
- 可以在setter和getter方法中添加断点进行代码检测,方便观察
- 在懒加载中,必须使用存取方法来设置,否则永远不会初始化成功
- 在初始化方法中,因为有可能会被子类重写存取方法,所以要直接访问成员变量
理解“对象等同性”这一概念
比较对象的等同行,有“==” 和 isEqual 两种方法,第一种是比较的两者的内存地址,第二种比较的是内容。
以“类族模式”隐藏实现细节
类族模式是提供一个基类的接口负责实例化某对象,通过其中的逻辑判断创建不同的字类对象,所以:创建出来的对象并非该类型。使用“==”判断恒定为假值。在系统的一些类族中,创建子类需要手动覆写其规定的方法,例如NSarray中需要覆写count,objectAtIndex方法。
在既有类中使用关联对象存放自定义数据
在系统提供的类的分类中,定义新的属性可以使用的方法,
-(void)setName:(NSString *)name{
objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)
}
-(NSString *)name{
objc_getAssociatedObject(<#id object#>, <#const void *key#>)
}
理解objc_msgSend的作用
Objective-C是基于运行时的语言,在调用[]方法时会转换成
objc_msgSend(<#id self#>, <#SEL op, ...#>,...)
该函数是参数可变的,主动调用需要引入#import <objc/message.h>头文件
例如:
- (void)viewDidLoad {
[super viewDidLoad];
objc_msgSend(self,@selector(haha)); // 打印haha
}
-(void)haha{
NSLog(@"haha");
}
理解消息转发机制
iOS消息转发分为三个步骤,第一步先查找是否动态添加了该方法,如果没有则进行第二步,查找备源接受者,如果没有执行第三步,封装查找不到的方法,直接把消息发送给目标对象。
用“方法调配”技术调配“黑盒方法”
iOS中消息列表的缓存是一个类似于字典的类型,会根据SEL映射IMP的地址,那么就可以通过交换映射地址的方法来实现变更系统自带API的功能。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Method m1 = class_getInstanceMethod([self class], @selector(haha));
Method m2 = class_getInstanceMethod([self class], @selector(hehe));
method_exchangeImplementations(m1, m2);
[self hehe]; //打印haha
}
-(void)hehe{
NSLog(@"hehe");
}
-(void)haha{
NSLog(@"haha");
}
使用方法调配技术需要的注意点就是方法内调用被交换的方法后悔死循环
理解“类对象”的用意
类和对象的经典的继承关系图
注意点是尽量使用==在判断类对象的类型是否相等,不要使用isEqual。