这两个月看了些OC底层一点的东西,还是有很多不明白的,为了加深印象,记录如下:
1、对象A的引用计数值存储于一张全局散列表中(未考虑tagged pointer优化),以A的地址&A为key,引用计数值减1为value。当A进行retain时,在全局散列表中根据&A找到对应的引用计数值,将其加1。
2、__weak修饰的所有对象存储于一张全局散列表中,例如A对象,若有__weak obj1 = A; __weak obj2 = A,则散列表中用&A为键,以一个类似数组的对象为value,该数组对象中存储了obj1和obj2。当A对象销毁时,到散列表中以&A为值找到对应数组,取出其中的obj1和obj2,将其指针清空为nil。
3、__weak修饰符只持有对象的弱引用,当在访问弱引用对象过程中,该对象有可能被废弃,存在危险。故当我们访问__weak修饰的对象时,系统会自动将其注册到autorelease中,保证我们在使用过程中该对象的生命周期。每一次访问弱引用对象都会将该对象注册进自动释放池一次,浪费性能。故现在流行如下写法。一来保证block执行过程中对象一定存在,二来不再重复访问弱引用修饰的对象,避免重复注册该对象到自动释放池。
1 NSObject *obj = [NSObject new]; 3 __weak typeof(obj) weakObj = obj; 5 void (^blk)() = ^{ 7 __strong typeof(weakObj) strongObj = obj; 9 if (strongObj) { 10 NSLog(@"%@",strongObj); 11 } 12 }; 14 blk();
4、代码片段
{ id obj = [NSMutableArray array]; }
上面用编译器模拟runtime代码可转换为如下:
id obj = objc_msgSend(NSMutableArray,@selector(array)); objc_retainAutoreleasedReturnValue(obj); obj_release(obj)
解读:对数组类NSMutableArray发送@selector(array)消息创建对象obj,retain对象让其存活,退出作用域销毁
而类方法+(id)array的实现则为
+ (id)array{ return [[NSMutableArray alloc] init]; }
模拟转换如下
+ (id)array{ id obj = objc_msgSend(NSMutableArray,@selector(array)); objc_msgSend(obj,@selector(init)); return objc_autoreleaseReturnValue(obj); }
解读:创建数组对象obj,将其放到自动释放池中后返回
纵观上面解析,发现数组对象创建后放到自动释放池中,然后retain,此时引用计数值为2。退出作用域强引用指针obj释放,则引用计数器减一。当此次runloop结束后自动释放池清空,数组对象再次release,引用计数器为0数组销毁。从代码上来看,该数组放入自动释放池跟retain完全没有必要,生成对象直接强指针持有,退出作用域销毁即可。
以下objc_autoreleaseReturnValue(): 用符号A代替、 objc_retainAutoreleasedReturnValue(): 用符号B代替
runtime优化:A和B总是成对出现,A总在B之前。对方法A和B进行了特殊处理,让其在如上情况下不是将对象放入自动释放池和将对象retain,而是动态判断,当发现A方法后紧随着B方法,则A方法不再将对象放入自动释放池,而是将一个标记为flag设置为YES。对B方法,检查标志位flag,若发现其位YES,则不再将对象retain,而只是将标记为flag重置为NO,省去两次操作。伪代码如下:
1 static BOOL flag = NO; 2 id objc_autoreleaseReturnValue(obj){ 3 if (@"后面紧跟着B方法") { 4 flag = YES; 5 }else{ 6 [obj autorelease]; 7 } 8 return obj; 9 } 10 11 void objc_retainAutoreleasedReturnValue(obj){ 12 if (flag) { 13 flag = NO; 14 }else{ 15 [obj retain]; 16 } 17 }
5、runtime一般用途
a、数据绑定,主要函数objc_setAssociatedObject和objc_getAssociatedObject,主要用于分类属性扩充
b、查看系统类的私有成员变量以及私有方法,可以针对系统私有变量进行KVC赋值。
c、黑盒方法method swizzling,跟换系统方法实现,比如在所有控制器的viewWillAppear和viewDidDisappear方法中添加友盟统计方法,统计页面停留时间
6、KVO实现原理:观察A对象的progress属性,系统动态创建A的子类B,将A的isa指针指向B,重写对象B的setProgress方法,在里面发出如下通知,通知观察者的相应方法
- (void)setPregress: (CGFloat )progress{ [self willChangeValueForKey:@"progress"]; _progress = progress; [self didChangeValueForKey:@"progress"]; }
7、同步线程死锁。如下代码在主线程执行会死锁
dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"会死锁"); });
同步函数dispatch_sync在block返回之前会一直卡住线程。若当前代码在主线程执行,则主线程在block返回之前一直会是堵塞状态。而block恰好是在主线程执行,而主线程被堵塞,于是blcok不会执行,block不会执行则不会返回,于是主线程一直堵塞。造成死锁。