首页 > 代码库 > iOS 8:【转】Block循环引用
iOS 8:【转】Block循环引用
源地址:http://fann.im/blog/2013/04/17/retain-cycle-in-blocks/
个人笔记,可能会有理解不够透彻而错误。 @fannheyward
Objective-C 是基于引用计数(retainCount)来做内存管理,ClassA 用到 ClassB 的时候,通过 alloc/retain/copy 等将 objectB.retainCount+1,不需要的时候通过 release/autorelease 将 objectB.retainCount-1. retainCount 归零后对象 objectB 被释放。假如 objectA retain objectB,objectB 反过来也 retain objectA,结果就是两者 retainCount 都无法归零,也就没办法被释放,造成内存泄露。这就是 Retain Cycle。
一般情况下注意避免两个对象互相 retain 就不太会出现 Retain Cycle,但是在用到 Blocks 的时候就要小心,很容易造成 Retain Cycle。这是因为 Blocks 会自动 retain 它引用的对象(block 里的对象),稍不留神就造成 Retain Cycle。文档: Object and Block Variables:
When a block is copied, it creates strong references to object variables used within the block. If you use a block within the implementation of a method:
- If you access an instance variable by reference, a strong reference is made to self;
- If you access an instance variable by value, a strong reference is made to the variable.
To override this behavior for a particular object variable, you can mark it with the __block storage type modifier.
MRC
一个简单的例子,Xcode 会报 Retain Cycle warning:
UIImageView *imgView = [[UIImageView alloc] initWithFrame:rect];[imgView setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { // ... imgView.image = image; // warning: Capturing ‘imgView‘ strongly in this block is likely to lead to a retain cycle}];
block 也是一个对象,[imgView setImageWithURL:completed:] 的时候 retain 了这个 block;而 block 又自动的 retain 了 imgView,所以就造成了 Retain Cycle。解决方法就是用 __block
告诉 block 不要 retain 引用的对象:
__block UIImageView *imgView = [[UIImageView alloc] initWithFrame:rect];[imgView setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { // ... imgView.image = image;}];
还有一种情况,block 里引用的对象是 self 或者 self.property,解决方法同理:
__block MyClass *myClass = self;operation.completeBlock = ^(NSInteger index) { [myClass doOther];};self.imgView = [[UIImageView alloc] initWithFrame:rect];__block UIImageView *tmpView = _imgView;[_imgView setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { tmpView.image = image;}];}
ARC
在 ARC 下不能用 __block
关键字,取而代之的是 __weak
或者 __unsafe_unretained
。其中 __weak
只能 iOS 5+ 使用,__unsafe_unretained
支持 iOS 4。如果 App 不需要考虑 4.x 用 __weak
会更好一些,__weak
修饰的对象释放后会被设置为 nil,而 __unsafe_unretained
会继续指向原来的内存。
__block MyClass *myClass = self; // MRC__weak MyClass *myClass = self; // ARC & iOS 5+__unsafe_unretained MyClass *myClass = self; // ARC & iOS 4.
一些参考文章:
- iOS blocks - 三個會造成retain cycle的anti patterns
- Friday Q&A 2010-04-30: Dealing with Retain Cycles
- ASIHTTPRequest Using blocks
- ARC - The meaning of __unsafe_unretained?
- IOS中的block和retain cycle
iOS 8:【转】Block循环引用