循环引用

循环应用
堆对堆的引用是产生循环引用的根本原因。
1、delegate与环
2、block与环
@property (nonatomic, copy) dispatch_block_t block;
self持有block,而堆上的block又会持有self,所以会导致循环引用,这个例子非常好,因为xcode都能检测出来,报出警告:[capturing self strongly in this block is likely to lead to a retain cycle],当然大部分循环引用的情况xcode是不会报警告的。解决这种循环引用的常用方式如下(这种解决方式可以解决大部分block引起的循环引用,但是有一定缺陷,且看下一节):
__weak typeof(self) weakSelf = self
block循环应用的深入:
1、weakSelf与其缺陷 //ClassB是一个UIViewController,假设从ClassA pushViewController将ClassB展示出来

@interface ClassB () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, strong) NSString *str; @end @implementation ClassB - (void)dealloc { } - (void)viewDidLoad { [super viewDidLoad]; self.str = @"111"; __weak typeof(self) weakSelf = self; self.block = ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", weakSelf.str); }); }; self.block(); }

这里会有两种情况:
  • 若从A push到B,10s之内没有pop回A的话,B中block会执行打印出来111。
  • 若从A push到B,10s之内pop回A的话,B会立即执行dealloc,从而导致B中block打印出(null)。这种情况就是使用weakSelf的缺陷,可能会导致内存提前回收。
2、weakSelf和strongSelf
@interface ClassB () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, strong) NSString *str; @end @implementation ClassB - (void)dealloc { } - (void)viewDidLoad { [super viewDidLoad]; self.str = @"111"; __weak typeof(self) weakSelf = self; self.block = ^{ __strong typeof(self) strongSelf = weakSelf; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", strongSelf.str); }); }; self.block(); }

我们发现这样确实解决了问题,但是可能会有两个不理解的点。
  • 【循环引用】这么做和直接用self有什么区别,为什么不会有循环引用:外部的weakSelf是为了打破环,从而使得没有循环引用,而内部的strongSelf仅仅是个局部变量,存在栈中,会在block执行结束后回收,不会再造成循环引用。
  • 这么做和使用weakSelf有什么区别:唯一的区别就是多了一个strongSelf,而这里的strongSelf会使ClassB的对象引用计数+1,使得ClassB pop到A的时候,并不会执行dealloc,因为引用计数还不为0,strongSelf仍持有ClassB,而在block执行完,局部的strongSelf才会回收,此时ClassB dealloc。
这样做其实已经可以解决所有问题,但是强迫症的我们依然能找到它的缺陷:
  • block内部必须使用strongSelf,很麻烦,不如直接使用self简便。
  • 很容易在block内部不小心使用了self,这样还是会引起循环引用,这种错误很难发觉。
3、@weakify和@strongify 查看github上开源的libextobjc库,可以发现,里面的EXTScope.h里面有两个关于weak和strong的宏定义。
问题1:loadView只会被调用一次,而viewDidLoad可能会被调用多次?
loadView v.s. viewDidLoad
按照我认知的viewDidLoad方法只执行一次,怎么会执行两次或者多次!
在内存不足时,viewDidLoad有可能被执行两次
问题2:不要在loadView中调用父类方法[super loadView],因为这会影响CPU性能。
深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用
.weak、strong的实现原理
在ARC环境下,id类型和对象类型和C语言其他类型不同,类型前必须加上所有权的修饰符。
所有权修饰符总共有4种:
1.strong修饰符 2.weak修饰符 3.unsafe_unretained修饰符 4.autoreleasing修饰符
一般我们如果不写,默认的修饰符是__strong。
要想弄清楚strong,weak的实现原理,我们就需要研究研究clang(LLVM编译器)和objc4 Objective-C runtime库了。
关于clang有一份关于ARC详细的文档,有兴趣的可以仔细研究一下文档里面的说明和例子,很有帮助。
嵌套block中的strongify和weakify 实验代码 得到结论。 看书找理论(C和指针)

    推荐阅读