Retain Cycle in Blocks

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。

MRC

一个简单的例子,Xcode 会报 Retain Cycle warning:

1
2
3
4
5
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 引用的对象:

1
2
3
4
5
__block UIImageView *imgView = [[UIImageView alloc] initWithFrame:rect];
[imgView setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
    // ...
    imgView.image = image;
}];

还有一种情况,block 里引用的对象是 self 或者 self.property,解决方法同理:

1
2
3
4
5
6
7
8
9
10
11
__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 会继续指向原来的内存。

1
2
3
__block MyClass *myClass = self;              // MRC
__weak MyClass *myClass = self;               // ARC & iOS 5+
__unsafe_unretained MyClass *myClass = self;  // ARC & iOS 4.

一些参考文章:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值