Block循環(huán)引用

概述

一般在使用Block時,為了防止循環(huán)引用,會使用__weak,因此下面主要驗證下是否在Block中必須使用__weak

驗證

  1. 新建一個類Car
    //Car.h
    #import <Foundation/Foundation.h>
    
    extern NSString *const CarNotificationName;
    typedef void(^SomeBlock)(void);
    
    @interface Car : NSObject
    
    @property (nonatomic, strong) SomeBlock myBlock;
    
    - (void)doWithBlock:(SomeBlock)block;
    - (void)carRunning;
    
    @end
     
    #import "Car.h"
    
    NSString *const CarNotificationName = @"carNotification";
    
    @implementation Car
    
    - (Car *)init {
        self = [super init];
        if (self) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveNotification) name:CarNotificationName object:nil];
        }
        return self;
    }
    
    - (void)receiveNotification {
        NSLog(@"Receiving Notification");
    }
    
    - (void)carRunning {
    }
    
    - (void)doWithBlock:(SomeBlock)block {
        self.myBlock = block;
        self.myBlock();
    }
    
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        NSLog(@"dealloc");
    }
    
    @end
    
    上面代碼主要是
    • 新建Car類,只要Car類實例沒有被銷毀,就會處理CarNotificationName的通知
    • 方法- (void)doWithBlock:(SomeBlock)block;接收一個SomeBlock類型的參數(shù),并被賦值給一個strong類型的屬性。
    • 方法- (void)dealloc;注銷通知,并打印信息
  2. 現(xiàn)在開始使用Car
    Car *car = [[Car alloc] init];
    [[NSNotificationCenter defaultCenter] postNotificationName:CarNotificationName object:nil];
    
    [car doWithBlock:^{
    
    }];
    
    car = nil;
    
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5);
    dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:CarNotificationName object:nil];
    });
    
    • 首先獲取Car類實例
    • 緊接著發(fā)送CarNotificationName通知,此時Car類實例會收到通知,并在控制臺輸出信息
    • 然后調(diào)用[car doWithBlock:^{}];方法,傳入的block中什么不做,然后將Car類實例設(shè)為nil,這時Car類實例被銷毀了
    • 然后使用 dispatch_after在5秒后發(fā)送CarNotificationName通知,此時由于Car類實例被銷毀了,因此控制臺不會有信息打印出來
  3. 如果此時對上面代碼做如下改動
    [car doWithBlock:^{
        [car carRunning];
    }];
    
    發(fā)現(xiàn)即使將Car類實例設(shè)為nil,5秒后Car類實例依然能夠接收CarNotificationName通知,并在控制臺打印了對應(yīng)的信息,也就是Car類實例并沒有被銷毀
  4. 原因就是循環(huán)引用產(chǎn)生
    Car類中對 SomeBlock是一個強(qiáng)引用
     @property (nonatomic, strong) SomeBlock myBlock;
    
    而在步驟3中的改動代碼中,在SomeBlock中反過來對Car類實例也進(jìn)行了強(qiáng)引用,這就構(gòu)成了一個環(huán),導(dǎo)致它們兩個不能被銷毀。

解決方案

只要將上述的引用環(huán)斷開其中的一個就可以了,因此有兩種解決方法

  1. 在Block中使用__weak
    Car * __weak weakCar = car;
    [car doWithBlock:^{
        [weakCar carRunning];
    }];
    
  2. Car類中對 SomeBlock弱引用
    @property (nonatomic, weak) SomeBlock myBlock;
    

結(jié)論

因此,可以確定不是在所有的Block中都要使用__weak,如果沒有構(gòu)成循環(huán)引用,或者對Block是弱引用就可以不必使用__weak
例如GCD或者[UIView animateWithDuration:5.0 animations:^{ }];中沒有構(gòu)成循環(huán)引用,就不必使用__weak,而在另一些情況如上述步驟3就需要使用__weak了。

備注

文中測試代碼已經(jīng)上到GitHub

參考

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

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

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