CC
半年 =_=
问题
最近碰到两个奇怪的问题,都是和引用计数相关的问题:
1.在dealloc方法里,打出CFGetRetainCount(self),结果总是1。retainCount==0 -> dealloc()?
2.在block中,strongSelf持有weakSelf,那么self和block是不是在循环引用?(这个问题来自http://www.jianshu.com/p/36342264d6df,评论中开心小锣鼓和Jason_Developer的问答)
第一个问题的答案
讲道理的话,按照文档所说,只有引用计数为0的时候,才会调用dealloc方法,释放object。又恰巧在看clang文档block那块的时候,有说weakSelf在执行第一个函数过程中时候,不会被释放掉。
那么说明,self在执行函数的时候,引用计数一定不能为0,也就是说,self在进入一个函数的时候,retainCount会+1;退出这个函数的时候,retainCount会-1。而这些ARC都帮我们做了。所以,在执行dealloc函数的时候,[self dealloc]也就意味着在dealloc里打出的CFGetRetainCount(self)始终都是1。没毛病。
生在红旗下,长在ARC里。好奇之前用MRC的前辈们是怎么做的,难道每个函数都要自己手写retain/release,或者每个函数里都要strong引用一下self?或者是严格管理生命周期,防止函数执行过程中对象释放?
第二个问题的答案
在设计搞清楚第二个问题的时候,才发现基础知识不扎实真的很伤。自己给自己挖了几个坑=_=
错误答案
使用strong修饰的指针引用使用weak修饰的object时,是不会增加计数的。
这个想法其实是我一直以来的一个误解,所以之前也搞过那种给object的某个成员外部赋值修饰为weak,内部再strong持有,并认为不影响其自身释放的错误。验证这个错误很简单:
ASWeakSelf(weakSelf);
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"%ld", CFGetRetainCount((__bridge CFTypeRef)self)); // self的retainCount+1
而且如果用一个strong的成员指向weakSelf,会造成自身循环引用,不会走到dealloc(这是我不死心再次尝试的结果)。其实用weakSelf指向self,再用strongSelf持有,只要在这个过程中self没释放,那么就跟直接用strongSelf引用self没有区别。
部分正确答案
strongSelf是在block中创建出的局部变量,所以block在执行完毕之后,strongSelf会释放,那么self的retainCount就会-1,从而不会影响到self的释放。而在strongSelf存在过程中,self和block是会想持有的(strongSelf==self),所以strongSelf可以维持在block执行期间self不被释放。
这一点我用以下代码验证了出来:
ASWeakSelf(weakSelf);
void (^block)(void) = ^(void) {
@autoreleasepool {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf doSomething];
NSLog(@"first = %ld", CFGetRetainCount((__bridge CFTypeRef)weakSelf));
}
NSLog(@"second = %ld", CFGetRetainCount((__bridge CFTypeRef)weakSelf));
};
后面打出的retainCount确实比前面那个少了1个,那么问题来了。为什么必须用weakSelf,直接用self扔到block里面去,局部变量持有,也是会释放的,所以这个事实好像对问题没啥效果。
脑残答案
自己绞尽脑汁(zhi)儿(er),灵光一闪,是不是有黑科技,比如weak记录了strong的引用之类的特殊处理。
这个问题很好解决,自己都觉得这样做太恶心,不够优雅,苹果怎么可能会做=_=||
最终正确答案
答案还是在一片网上一片博客找到的——http://blog.csdn.net/u010130947/article/details/50552910(大神闪亮登场)!看了这篇博客,我就想来了在前年我刚接触iOS时看的Object-C那本书,书中有详细介绍闭包的上下文拷贝特性,已然忘光(“无忌,学的怎样了?” “忘了一半了。” “现在呢?” “已经忘光了。” “好!啊哈哈哈哈!”)。
通常在block中用到的常量会拷贝,变量的retainCount会+1(不全是,详见上述大神博客),就是为了保证block的完美运行。所以,如果有用到self或者直接引用self中的全局成员变量,self的retainCount都会+1。而在block释放,self的这一个count才能减掉。而weakSelf被block使用,block无法增加其retainCount,也就无需减去一个retainCount。
而在block内部再用strongSelf引用weakSelf,就回到了“部分正确答案”中的逻辑。
所以,block持有的核心在于block保存上下文 [捂脸]
PS
在查问题的时候,还遇到一个小问题:
void (^block)(void) = ^(void) {
[self doSomething];
NSLog(@"haha");
};
self.block = block;
每次执行了以上代码,self的retainCount都会+2。而看了大神文章,了解到ARC会自动将NSStackBlock拷贝成NSMallocBlock,说明其实生成了两个block。用@autoreleasepool把上述代码包起来,执行完之后再看retainCount,确实是只+1,所以验证了大神的结论(autoreleasepool提前释放掉了NSStackBlock)。