Block 代码块中防止self循环引用的问题

作者: 多飞 | 来源:发表于2016-04-25 11:48 被阅读1239次

一、什么是循环引用

循环引用指两个对象相互强引用了对方,即retain了对方,从而导致谁也释放不了谁的内存泄露问题。如声明一个delegate一般用assign而不能用retainstrong,因为你一旦那么做了,很大可能引起循环引用。在以往的项目中,我几次用动态内存检查发现了循环引用导致的内存泄露。

二、block中的循环引用

这里讲的是block的循环引用问题,因为block在拷贝到堆上的时候(为什么要拷贝到堆上?见下面补充),会retain其引用的外部变量,那么如果block中如果引用了他的宿主对象,那很有可能引起循环引用,如:

- (void)dealloc
{
NSLog(@"no cycle retain");
}
- (id)init
{
self = [super init];
if (self) {

#if TestCycleRetainCase1
    //会循环引用
    self.myblock = ^{

        [self doSomething];
    };
#elif TestCycleRetainCase2

    //会循环引用
    __block TestCycleRetain *weakSelf = self;
    self.myblock = ^{

        [weakSelf doSomething];
    };

#elif TestCycleRetainCase3

    //不会循环引用
    __weak TestCycleRetain *weakSelf = self;
    self.myblock = ^{

        [weakSelf doSomething];
    };

#elif TestCycleRetainCase4

    //不会循环引用
    __unsafe_unretained TestCycleRetain *weakSelf = self;
    self.myblock = ^{

        [weakSelf doSomething];
    };

#endif
    NSLog(@"myblock is %@", self.myblock);
}
return self;
}
- (void)doSomething
{
NSLog(@"do Something");
}
int main(int argc, char *argv[]) {
    @autoreleasepool {
    TestCycleRetain* obj = [[TestCycleRetain alloc] init];
    obj = nil;
    return 0;
    }
}

经过上面的ARC环境测试发现,在加了__weak__unsafe_unretained的变量引入后,TestCycleRetain方法可以正常执行dealloc方法,而不转换和用__block转换的变量都会引起循环引用。
但是实际情况是:
1)MRC情况下,用__block可以消除循环引用。
2)ARC情况下,必须用弱引用才可以解决循环引用问题,iOS 5之后可以直接使用__weak,之前则只能使用__unsafe_unretained了,__unsafe_unretained缺点是指针释放后自己不会置空。

示例代码:(关于使用block会防止循环引用的代码示例)
1)在ARC下,由于__block抓取的变量一样会被Block retain,所以必须用弱引用才可以解决循环引用问题,iOS 5之后可以直接使用__weak,之前则只能使用__unsafe_unretained__unsafe_unretained缺点是指针释放后自己不会置空。示例代码:

//iOS 5之前可以用__unsafe_unretained

//__unsafe_unretained typeof(self) weakSelf = self;

__weak typeof(self) weakSelf = self;

self.myBlock = ^(int paramInt)

{

//使用weakSelf访问self成员

[weakSelf anotherFunc];

};
2)在非ARC下,显然无法使用弱引用,这里就可以直接使用__block来修饰变量,它不会被Block所retain的,参考代码:

//非ARC

__block typeof(self) weakSelf = self;

self.myBlock = ^(int paramInt)

{

//使用weakSelf访问self成员

[weakSelf anotherFunc];

};

三、补充:为甚么要将block要拷贝到堆上?

一般来说我们总会在设置Block之后,在合适的时间回调Block,而不希望回调Block的时候Block已经被释放了,所以我们需要对Block进行copycopy到堆中,以便后用。
当一个BlockCopy的时候,如果你在Block里进行了一些调用,那么将会有一个强引用指向这些调用方法的调用者(也就是上面讲的,会存在循环引用导致内存泄露的风险),其有两个规则:
如果你是通过引用来访问一个实例变量,那么将强引用至self
如果你是通过值来访问一个实例变量,那么将直接强引用至这个“值”变量

相关文章

网友评论

    本文标题:Block 代码块中防止self循环引用的问题

    本文链接:https://www.haomeiwen.com/subject/fzafrttx.html