理解 weak-strong dance
问题
大家都知道使用block的时候可以通过__weak关键字来避免循环引用,如下:
1 |
|
1 | __weak typeof(self) weakSelf = self; |
这时handler持有了block对象,捕获了weakSelf,延长了这个局部变量的生命周期,其实block内外两个weakSelf的变量值虽然一样,但变量地址不同,如浅拷贝,对self的引用都是弱引用,当前self被释放(如pop出当前控制器),weakSelf就指向了nil。
先说说block的情况:
手动调用 Block 的实例方法copy
Block 作为函数返回值返回
将 Block 赋值给附有__strong修饰符的成员变量
在方法名中含有usingBlock的 Cocoa 框架方法或 GCD 的 API 中传递 Block
以上四种情况,如果 Block 在栈上,那就复制一份到堆上,如果 Block 已经在堆上,那就把该 Block 的引用计数加1。
也就是说如果将以上的block放到gcd中运行耗时或延时操作过程中,self被释放了,block还被引用着,如果这是讲self(nil)作为参数传入,就会导致crash。
解决
1 | NSLog(@"block执行中,Self is %@", weakSelf); |
通过在 block 里对 weakSelf 进行强引用,间接对 self 进行强引用(网上资料说 self 的引用计数会加1),而当 block 执行完毕,strongSelf 被释放,self 的引用计数会减1。这样就可以既避免循环引用,也防止了 self 引用过程中被提前释放。
这样写还是有风险,在 block 之前,self 就被释放了,weakSelf 为 nil,那么strongSelf 的变量值也是 nil,还是会crash。strongSelf 只能保证 self 被使用过程中不会被提前释放,所以要使用之前还是要判断 strongSelf 是否为 nil。