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.

一些参考文章:

  • 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学习资料,iOS)