关于block坑爹的retain Cycle
2015-01-08 17:49
211 查看
http://sealedace.com/blog/2014/01/23/block-retain-cycle/
在进入正题之前,建议读一下船哥的文章——iOS中消息的传递机制。里面除了描述iOS中消息的传递机制,还提供了一些思路帮助大家正确选择某种传递方法来完成开发工作。而本篇文章的主题是跟block相关的。
我们知道在使用block时,必须避免出现
(下文译自The Correct Way to Avoid Capturing Self in Blocks With ARC,有小改动)
以前避免
所有在block中被访问的变量都会被block保留,而上面这段代码的避免了对
一个常见的(naive)方法就是像下面的代码一样去创建一个
其实,这个跟之前的那个方法差不多是一样的,只是关键字变成了
问题是,当
嗯,如果block中的代码就这样简单,没有复杂的消息传递就结束了,也许倒也没什么问题。但是,假如
请看下面这一段看起来感觉跟之前差不多的代码:
可以看到,这里我们是直接访问成员变量(很糟糕的方式),而没有通过Objective-C里面的推荐的成员访问器。这样写仍然会先访问到
所以,如果
鉴于这些问题,最安全的做法是在block外部对
另外,澄清一点,这段代码并不能保证线程安全,你仍然需要添加
在用block的过程中,不要只对看得到
欢迎大家纠错(-0_0)
在进入正题之前,建议读一下船哥的文章——iOS中消息的传递机制。里面除了描述iOS中消息的传递机制,还提供了一些思路帮助大家正确选择某种传递方法来完成开发工作。而本篇文章的主题是跟block相关的。
我们知道在使用block时,必须避免出现
retain cycle。如果写代码不仔细造成了
retain cycle,就会出现内存泄露。即使Xcode有静态代码分析工具,但很多时候Xcode也不太靠谱,根本什么提示都没有,所以还是自己写代码多注意比较好。
(下文译自The Correct Way to Avoid Capturing Self in Blocks With ARC,有小改动)
以前避免
retain cycle的方法大概像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 | @implementation Foo @property (strong) Bar *bar; @property (strong) Baz *baz; -(void)aMethod { __block Foo *blockSelf = self; bar.block = ^ { [blockSelf.baz doSomething]; }; } @end |
self的直接引用。如果self持有这个block——直接或者间接,都会导致
retain cycle。因此,我们使用了
__block关键字来创建一个对
self的临时引用(非保留),我们在block会使用这个引用代替
self来操纵对象。但是很不幸,ARC出现后废弃了这种方法并且将
__block作用改成了跟
strong关键字一样。所以,我们需要一个新的方法来解决这个问题。
一个常见的(naive)方法就是像下面的代码一样去创建一个
weak引用:
1 2 3 4 5 6 7 8 9 10 11 12 1314 | @implementation Foo @property (strong) Bar *bar; @property (strong) Baz *baz; -(void)aMethod { __weak Foo *blockSelf = self; bar.block = ^ { [blockSelf.baz doSomething]; }; } @end |
__weak。用这个引用放在block里面运行,没有
retain cycle。但是不管怎样,这样做仍然有一些问题。
问题是,当
self在block执行期间或block执行之前被销毁了,会怎样呢?我们乍一看应该是没什么问题的:因为
blockSelf是一个
weak引用,它在销毁后会自动置空,而且对nil发送消息是不会发生任何情况的。对吧?
嗯,如果block中的代码就这样简单,没有复杂的消息传递就结束了,也许倒也没什么问题。但是,假如
self在这个block执行的期间,在其他线程里面被销毁了呢?假如在你block里面工作完成到一半的时候
self就释放置nil了呢?难道你就这样放弃block中要做的事情,就这样丢着不管了吗?是啊!你这样做也许没有什么损失。呵呵,但是这里还真有一种更加糟糕情况潜伏着…
请看下面这一段看起来感觉跟之前差不多的代码:
1 2 3 4 5 6 7 8 9 10 11 12 1314 | @implementation Foo @property (strong) Bar *bar; @property (strong) Baz *baz; -(void)aMethod { __weak Foo *blockSelf = self; bar.block = ^ { [baz doSomething]; }; } @end |
self,然后才能访问它的成员变量。如果你这样做的话,runtime没有使用成员访问器,而是使用了C级别的空指针引用。换种方式来看,上面的代码会变成大概下面这个样子:
1 2 3 4 5 6 7 8 9 10 11 12 1314 | @implementation Foo @property (strong) Bar *bar; @property (strong) Baz *baz; -(void)aMethod { __weak Foo *blockSelf = self; bar.block = ^ { [self->baz doSomething]; }; } @end |
self在block执行的途中被销毁了,呵呵,你就会遇到一个因为使用了空指针来访问成员变量而导致的crash。这种情况,跟对nil发送消息可不一样,因为你根本就是在对一个不存在的东西发送消息。
鉴于这些问题,最安全的做法是在block外部对
self先获取一个
weak引用,然在block内部执行代码的开始,创建一个对
self的
strong引用——这样在block执行期间,你不用担心
self被销毁。然后等你不需要用到
self的
strong引用时候,可以将
strongSelf置nil,或者干脆不管,等待代码执行结束退出(ARC,你懂的)。如果你按照这个方法做了,应该不会有什么问题了。所以,代码结构大致应该是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 1314 | @implementation Foo @property (strong) Bar *bar; @property (strong) Baz *baz; -(void)aMethod { __weak Foo *blockSelf = self; bar.block = ^ { Foo *strongSelf = blockSelf [strongSelf.baz doSomething]; }; } @end |
@synchronize域或者线程锁来保证线程安全。不过可以肯定的是,这样写一定比文章中提到的存在问题的代码要安全的多!
经验小结
在用block的过程中,不要只对看得到self加
weak引用,如果你像文章里那样直接访问成员变量,也是同样会存在
retain cycle的问题。解决问题办法也很简单,使用文章里提到的解决方案,或者干脆把大段的代码移出block,这样反而更好些。
欢迎大家纠错(-0_0)
相关文章推荐
- 关于display:inline-block
- 关于block中数据的存储和重组的探究
- 关于 display: inline 、block 、inline-block
- oracle form builder :关于Form/block/canvas/window
- 一道关于block尺寸计算的笔试题
- 关于block change tracking的那些事
- 关于 db block gets,consistent gets,physical reads的概念
- 关于 display: inline 、block 、inline-block
- 关于registerstartupscript与RegisterClientScriptBlock的区别
- 关于执行计划里recursive calls,db block gets和consistent gets参数的解释
- 关于send时产生WSAEWOULDBLOCK的处理
- 关于 display: inline 、block 、inline-block (一)
- Hadoop0.1.0 关于NameNode如何保证每一个Block的Replicas为配置文件指定的个数问题
- 关于CSS属性display:inline-block的深入理解
- oracle form builder :关于Form/block/canvas/window
- [Quicky] block 和 inline 答案揭晓~ 另付一则,关于 word-break
- 关于 display: inline 、block 、inline-block
- (转)关于WSAEWOULDBLOCK
- 关于block change tracking
- 关于inline-block