什麼是循環引用
循環引用指兩個對象互相強引用了對方,既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
- 如果你是通過值來通路一個執行個體變量,那麼将直接強引用至這個“值”變量