《Objective-C高級編程》Blocks 閱讀筆記系列
《Objective-C高級編程》Blocks 閱讀筆記 item1(Blocks概要和模式)
《Objective-C高級編程》Blocks 閱讀筆記 item2(Block的實(shí)質(zhì))
《Objective-C高級編程》Blocks 閱讀筆記 item3(截獲自動變量值)
《Objective-C高級編程》Blocks 閱讀筆記 item4(__block說明符)
《Objective-C高級編程》Blocks 閱讀筆記 item5(Block存儲域)
《Objective-C高級編程》Blocks 閱讀筆記 item6(__block變量存儲域)
《Objective-C高級編程》Blocks 閱讀筆記 item7(截獲對象)
《Objective-C高級編程》Blocks 閱讀筆記 item8(__block變量和對象)
《Objective-C高級編程》Blocks 閱讀筆記 item9(Block循環(huán)引用)
《Objective-C高級編程》Blocks 閱讀筆記 item10(copy/release實(shí)例方法)
2.3 Blocks的實(shí)現(xiàn)
2.3.8 Block循環(huán)引用
*** 如果在Block中使用附有__strong修飾符的對象類型自動變量钱磅,那么當(dāng)Block從棧復(fù)制到堆時,該對象為Block所持有。 *** 這樣容易引起*** 循環(huán)引用 ***瘪匿。
*** 使用Block類型成員變量和附有__strong修飾符的self出現(xiàn)循環(huán)引用 ***
typedeft void (^blk_t)(void);
@interface MyObject : NSObject
{
blk_t blk_;
}
@end
@implementation MyObject
- (id)init
{
self = [super init];
blk_ = ^{NSLog(@"self = %@", self);};
return self;
}
- (void)dealloc
{
NSLog(@"dealloc");
}
@end
int main()
{
id o = [[MyObject alloc] init];
NSLog(@"%@", o);
return 0;
}
編譯該源代碼時稻扬,編譯器會發(fā)出警告,這是因?yàn)槌霈F(xiàn)了循環(huán)引用,從而導(dǎo)致dealloc實(shí)例方法沒有被調(diào)用呼伸。
*** 使用Block類型成員變量和附有__weak修飾符的self避免循環(huán)引用 ***
為避免此循環(huán)引用虱痕,可聲明附有__weak修飾符的變量纲熏,并將self賦值給該變量,然后在Block語法中使用該變量健提。
- (id)init
{
self = [super init];
id __weak weakSelf = self;
blk_ = ^{NSLog(@"self = %@", weakSelf);};
return self;
}
*** 面向iOS4蝶怔,使用__unsafe_unretained修飾符 ***
- (id)init
{
self = [super init];
id __unsafe_unretained weakSelf = self;
blk_ = ^{NSLog(@"self = %@", weakSelf);};
return self;
}
面向iOS4,使用__unsafe_unretained修飾符替代__weak修飾符,并且不用擔(dān)心懸掛指針問題捎迫。
*** Block中沒有使用self晃酒,但是截獲了self ***
@interface MyObject : NSObject
{
blk_t blk_;
id obj_;
}
@end
@implementation MyObject
- (id)init
{
self = [super init];
blk_ = ^{NSLog(@"obj_ = %@", obj_);};
return self;
}
該源代碼,如果編譯窄绒,編譯器會發(fā)出警告(出現(xiàn)循環(huán)引用)贝次。這是因?yàn)锽lock語法中使用的obj_實(shí)際上截獲了self,而對編譯器來說彰导,obj_只不過是對象的結(jié)構(gòu)體的成員變量蛔翅。
blk_ = ^{NSLog(@"obj_ = %@", self->obj_);};
為避免循環(huán)引用,解決方法參考前面位谋。
*** 使用__block變量避免循環(huán)引用 ***
typedeft void (^blk_t)(void);
@interface MyObject : NSObject
{
blk_t blk_;
}
@end
@implementation MyObject
- (id)init
{
self = [super init];
__block id blockSelf = self;
blk_ = ^{
NSLog(@"self = %@", blockSelf);
blockSelf = nil; // 記得清零
};
return self;
}
- (void)execBlock
{
blk_();
}
- (void)dealloc
{
NSLog(@"dealloc");
}
@end
int main()
{
id o = [[MyObject alloc] init];
[o execBlock];
return 0;
}
該源代碼沒有引起循環(huán)引用山析。但是,如果不調(diào)用execBlock實(shí)例方法(即不執(zhí)行賦值給成員變量blk_的Block)掏父,便會循環(huán)引用并引起內(nèi)存泄露笋轨。該種循環(huán)引用可參看下面。
*** 使用__block變量不恰當(dāng)會出現(xiàn)循環(huán)引用 ***
在生成并持有MyObject類對象的狀態(tài)下會引起以下循環(huán)引用赊淑,如下圖:
- MyObject類對象持有Block
- Block持有__block變量
- __block變量持有MyObject類對象
如果不執(zhí)行execBlock實(shí)例方法爵政,就會持續(xù)該循環(huán)引用從而造成內(nèi)存泄露。
如果只想execBlock實(shí)例方法陶缺,Block被執(zhí)行钾挟,nil被賦值在__block變量blockSelf中。
blk_ = ^{
NSLog(@"self = %@", blockSelf);
blockSelf = nil; // 記得清零
};
因此饱岸,__block變量blockSelf對MyObject類對象的強(qiáng)引用失效等龙,從而避免了循環(huán)引用,如下圖:
- MyObject類對象持有Block
- Block持有__block變量
*** 使用__block變量避免循環(huán)引用的優(yōu)缺點(diǎn) ***
優(yōu)點(diǎn)
- 通過__block變量可控制對象的持有期間
- 在不能使用__weak修飾符的環(huán)境中不使用__unsafe_unretained修飾符即可(不必?fù)?dān)心懸掛指針)
在執(zhí)行Block時可動態(tài)地決定是否將nil或其他對象賦值在__block變量中伶贰,從而避免出現(xiàn)循環(huán)引用蛛砰。
缺點(diǎn)
- 為避免循環(huán)引用必須執(zhí)行Block
存在執(zhí)行了Block語法,卻不執(zhí)行Block的路徑時黍衙,無法避免循環(huán)引用泥畅。
總結(jié)
若由于Block引發(fā)了循環(huán)引用時,根據(jù)Block的用途選擇使用__block變量琅翻、__weak修飾符或__unsafe_unretained修飾符來避免循環(huán)引用位仁。