天天看點

Block 代碼塊中循環引用問題什麼是循環引用block中的循環引用為甚麼要将block要拷貝到堆上

什麼是循環引用

循環引用指兩個對象互相強引用了對方,既retain了對方,進而導緻誰也釋放不了誰的記憶體洩漏問題。比如我們在代理中一般用weak來修飾delegate而不用strong,正是因為那樣會造成循環引用導緻代理無法釋放的問題。

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 ;
    }
}
           

經過上面的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];

};
)在非ARC下,顯然無法使用弱引用,這裡就可以直接使用__block來修飾變量,它不會被Block所retain的,參考代碼:

//非ARC

__block typeof(self) weakSelf = self;

self.myBlock = ^(int paramInt)

{

//使用weakSelf通路self成員

[weakSelf anotherFunc];

};
           

為甚麼要将block要拷貝到堆上

一般來說我們總會在設定Block之後,在合适的時間回調Block,而不希望回調Block的時候Block已經被釋放了,是以我們需要對Block進行copy,copy到堆中,以便後用。

當一個Block被Copy的時候,如果你在Block裡進行了一些調用,那麼将會有一個強引用指向這些調用方法的調用者(也就是上面講的,會存在循環引用導緻記憶體洩露的風險),其有兩個規則:

  • 如果你是通過引用來通路一個執行個體變量,那麼将強引用至self
  • 如果你是通過值來通路一個執行個體變量,那麼将直接強引用至這個“值”變量

繼續閱讀