許多博客文章和自己接手的項(xiàng)目中對(duì)block循環(huán)引用問(wèn)題理解不完全正確。也是最近, 在自己以為已經(jīng)完全理解了的情況下,突然看了下RactiveCocoa框架里面對(duì)@weakify和@strongify使用,又產(chǎn)生了疑問(wèn),于是在閱讀了block本質(zhì)相關(guān)博客后,結(jié)合函數(shù)調(diào)用在匯編里面的實(shí)現(xiàn),終于算是理解了。
防止循環(huán)引用的方式:
方式一: __weak
__weak typeof(self) weakSelf = self;
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[weakSelf requestData];//
}];
這種方式的弊端是block里面使用的是弱引用的self,所以存在當(dāng)有關(guān)self的某條指令正在執(zhí)行過(guò)程中,self對(duì)象已銷(xiāo)毀,無(wú)法確保block里面執(zhí)行完再銷(xiāo)毀,可能會(huì)導(dǎo)致crash。所以蘋(píng)果官方給出的解決方式是下面的方式二
方式二:strongSelf
__weak typeof(self) weakSelf = self;
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf requestData];//
}];
這種方式是在block被執(zhí)行時(shí),由聲明的局部變量strongSelf強(qiáng)引用weakSelf,block本質(zhì)是函數(shù)調(diào)用,函數(shù)調(diào)用時(shí)局部變量在調(diào)用完之后會(huì)自動(dòng)銷(xiāo)毀,所以__strong typeof(weakSelf) strongSelf = weakSelf;代碼能保證block調(diào)用過(guò)程的安全性,也能保證在block調(diào)用完之后解除強(qiáng)引用,解決了循環(huán)引用問(wèn)題。
方式三:@weakify和@strongify
使用@weakify和@strongify,這個(gè)在RactiveCocoa的RACEXTScope.h里面有定義。@weakify等價(jià)于__weak typeof(self) weakSelf = self。@strongify等價(jià)于__strong typeof(weakSelf) strongSelf = weakSelf; 下面是RactiveCocoa使用的范例
@implementation UIControl (RACSignalSupport)
- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {
@weakify(self);
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
@strongify(self);
[self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
[self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
[subscriber sendCompleted];
}]];
return [RACDisposable disposableWithBlock:^{
@strongify(self);
[self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
}];
}]
setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", self.rac_description, (unsigned long)controlEvents];
}
@end
在block內(nèi)部還有block時(shí),如果存在循環(huán)引用問(wèn)題,再次使用@strongify(self)才是正確的方式。因?yàn)槿绻苯佑胹elf,那么內(nèi)部的那個(gè)block首先是被持有它的對(duì)象強(qiáng)引用的,然后這個(gè)對(duì)象的存在與否會(huì)決定self是否能正常釋放 。
循環(huán)引用產(chǎn)生的原因
block如果沒(méi)有被copy到堆區(qū)時(shí),他是一個(gè)棧block,block在調(diào)用一次就銷(xiāo)毀了,這種情況不會(huì)造成循環(huán)引用,比如Masonry框架里面的添加約束block
block被
對(duì)象A引用了,此時(shí)block是存在于內(nèi)存中的。block中用到的變量會(huì)被強(qiáng)引用,在block釋放后才會(huì)釋放;如果對(duì)象A被block中的變量強(qiáng)引用了,那么就會(huì)形成類(lèi)似self->對(duì)象A->block->self這樣的環(huán)。打破這樣的循環(huán)引用推薦采用方式三
正確使用的結(jié)論
block調(diào)用本質(zhì)是函數(shù)調(diào)用,block會(huì)捕獲并引用內(nèi)部的變量,
- 如果是從??截惖蕉训腷lock需要注意循環(huán)引用的問(wèn)題,在block之前對(duì)會(huì)造成循環(huán)引用的變量使用weak指針,包括循環(huán)引用和單例對(duì)象的block;
- 同時(shí)為了保證block調(diào)用過(guò)程不會(huì)因?yàn)閟elf被置位nil而導(dǎo)致可能的crash,需要在block里面再?gòu)?qiáng)引用self,保證在block調(diào)用之后才釋放self。
- 如果是在block里面還有block的情況下,最好的解決方法是在第二個(gè)block中再次使用@strongify(self)。這個(gè)也是RAC里面源碼采用的方式
參考文章:
Block原理淺析,循環(huán)引用的產(chǎn)生方式
循環(huán)引用原因分析
block里的self、weakSelf、strongSelf
Reactive Cocoa中的@weakify、@strongify是如何裝逼的