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