正確使用@weakify 和@strongify防止block循環(huán)引用

許多博客文章和自己接手的項(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)生的原因
  1. block如果沒(méi)有被copy到堆區(qū)時(shí),他是一個(gè)棧block,block在調(diào)用一次就銷(xiāo)毀了,這種情況不會(huì)造成循環(huán)引用,比如Masonry框架里面的添加約束block

  2. 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)。

  3. 打破這樣的循環(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是如何裝逼的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容