Block與weakSelf和StrongSelf

不想廢話,直接上代碼進(jìn)入主題,從基本的循環(huán)引用說起:

在ViewController中聲明一個(gè)block屬性,如下:

@interface ViewController ()

@property (nonatomic, assign) NSInteger times;
@property (nonatomic, copy) void(^block)();

@end

在viewdidLoad中,我們來模擬一下循環(huán)引用:

- (void)viewDidLoad {
    [super viewDidLoad];
  
    self.times = 0;
    self.block = ^{
      self.times += 3;
         NSLog(@"%@---%ld", self, self.times);
    });
    self.block();
}

- (void)dealloc
{
    NSLog(@"成功銷毀");
}

上面這段代碼便造成了循環(huán)引用:self對(duì)block屬性有一條強(qiáng)引用,block中又要捕獲_times實(shí)例變量,所以必須必須得保留self,即編譯器自動(dòng)對(duì)self的引用計(jì)數(shù)+1,這就形成了self —> block -> self的"保留環(huán)"。即便pop出當(dāng)前VC,因?yàn)樵摫A舡h(huán)的存在,我們可以看到dealloc方法不會(huì)被調(diào)用,這塊內(nèi)存也不會(huì)被銷毀,這就造成了內(nèi)存泄漏。

這種情況下,一般我們的解決方案是,把該保留環(huán)的一條強(qiáng)指針弱化,一般是:在block前加上:__weak type(self) weakSelf = self

即viewDidLoad里面的代碼變成:

- (void)viewDidLoad {
    [super viewDidLoad];
  
    self.times = 0;
    __weak type(self) weakSelf = self;
    self.block = ^{
      weakSelf.times += 3;
         NSLog(@"%@---%ld", weakSelf, weakSelf.times);
    });
    self.block();
}

一般情況下,這么做完全沒問題,但是在下面這種情況下,就出問題了:

    __weak typeof(self) weakSelf = self;
    self.block = ^{
      // 延遲執(zhí)行Block里面的內(nèi)容
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            weakSelf.times += 3;
            NSLog(@"%@---%ld", weakSelf, weakSelf.times);
        });
    
    };
  
    self.block();

這段代碼表示:我block里面的代碼異步延遲執(zhí)行;
我們?cè)趧俻ush進(jìn)VC的時(shí)候3s內(nèi)又pop出去,發(fā)現(xiàn)雖然dealloc方法被調(diào)用了,程序也沒有崩潰,但是發(fā)現(xiàn)weakSelf變?yōu)榱薾ull。對(duì)_times的+3操作無效,依然為0;

這種情形下問題就出現(xiàn)了,也就是我們Block里面的內(nèi)容還沒執(zhí)行完畢的時(shí)候,當(dāng)前控制器已經(jīng)被pop,因?yàn)閟elf是弱指針,block就不能保留self,self的引用計(jì)數(shù)不在+1。那么self不被強(qiáng)指針引用,當(dāng)然會(huì)變成nil。這就造成了問題,那么怎么辦呢?

這種情況下,就應(yīng)該在block里面讓block生成一個(gè)自動(dòng)變量保持對(duì)這個(gè)弱self的強(qiáng)引用,讓其不會(huì)被銷毀,即:

__weak typeof(self) weakSelf = self;
    self.block = ^{
        __strong typeof(self) strongSelf = weakSelf;
    
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
            
            strongSelf.times += 3;
            NSLog(@"%@", strongSelf);
        });
    
    };
    self.block();

這樣,即使在3s內(nèi)當(dāng)前控制器被pop,那么block依然會(huì)執(zhí)行,_times的值也順利被+3,問題也就解決了。有人可能就不理解了,你又把self變回強(qiáng)指針,那么又會(huì)回到原來的循環(huán)引用了嗎?其實(shí)不然,這里的strongSelf和self已經(jīng)不是一個(gè)東西了,它對(duì)block是沒有引用的。這個(gè)strongSelf是在block內(nèi)部聲明的局部變量,只有block對(duì)他有引用,當(dāng)block被銷毀時(shí),它也會(huì)跟著被銷毀。所以,這就完美的解決了循環(huán)引用的問題。

當(dāng)然開發(fā)中為了圖省事兒,一般都把該兩句代碼定義為宏,方便直接使用:

 #define weakly(objc, weakObjc) __weak typeof(objc) weakObjc = objc;
 #define strongly(objc, strongObjc) __strong typeof(objc) strongObjc = objc;

weakly(self, weakSelf);
self.block = ^{
 strongly(weakSelf, strongSelf);
 [strongSelf doSomething];
});

或者你也可以在libextobjc這個(gè)開源庫中,使用

#import "EXTScope.h"

@weakify(self)
self.block = ^{
 @strongify(self)
 [self doSomething];
});

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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