__weak與__strong是如何解決循環(huán)引用的

當我們寫一個block時,如果你的block涉及被self持有以及需要訪問self的成員時示辈,循環(huán)引用問題由此產(chǎn)生太援。解決的辦法也很簡單,其中利用__weak與__strong是常見的手段匪蟀,類似代碼如下:


__weak typeof(self) weakSelf =self;

self.block= ^{

__strong typeof(weakSelf) strongSelf = weakSelf;

[strongSelfprint];

};

如果要把這個問題說清楚明白,不是件容易的事宰僧,看了很多文章材彪,都沒有深入全面地講明白這個問題。為了深入剖析其中的原理,構建了一個MyTestBlock類段化,類的結構代碼如下:

#import <Foundation/Foundation.h>  
@interface MyTestBlock : NSObject  
@property (nonatomic, copy) void(^block)();  

@end
#import "MyTestBlock.h"  
  
@implementation MyTestBlock  
  
- (void)dealloc  
{  
    NSLog(@"------>dealloc");  
}  
  
- (instancetype)init  
{  
    self = [super init];  
    if (self)  
    {  
        __weak typeof(self) weakSelf = self;  
        self.block = ^{  
            __strong typeof(weakSelf) strongSelf = weakSelf;  
            [strongSelf print];  
        };  
    }  
    return self;  
  
}  
  
- (void)print  
{  
    NSLog(@"---->print");  
}  
@end

然后在main方法里測試它:

#import <Foundation/Foundation.h>  
#import <objc/runtime.h>  
#import "MyTestBlock.h"  
  
int main(int argc, const charchar * argv[])  
{  
    @autoreleasepool  
    {  
        MyTestBlock *testBlock = [[MyTestBlock alloc] init];  
        testBlock.block();  
    }  
    while (1) {;} //讓主程序一直運行不退出  
    return 0;  
}  

代碼中嘁捷,block屬性為copy,定義為copy屬性是一個存放在堆的block穗泵,堆block從棧復制過程中會復制它所使用的變量的引用(非__block屬性)普气,循環(huán)引用也是因此產(chǎn)生。畫了個簡單的示意圖佃延,如下:

對于代碼中的這一步现诀,是如何打破這個循環(huán)的呢?我們在block外的weakSelf構造了一個指向self的弱引用履肃,如下:

__weak typeof(self) weakSelf = self;  

如下圖所示:

如果你的block之行不是異步執(zhí)行仔沿,在顯示調(diào)用testBlock.block()時,就已經(jīng)執(zhí)行尺棋,此時的strongSelf變量是可以省略的封锉,如下:

self.block = ^{  
   [weakSelf print];  
};

打印結果如下:

2017-02-03 12:13:35.881 myBlock[6131:4562526] ---->print  
2017-02-03 12:13:35.883 myBlock[6131:4562526] ------>dealloc

雖然循環(huán)引用就打破了,但是新的問題又來了膘螟。那就是會有self提前于block執(zhí)行之前釋放的場景成福,testBlock實體釋放了,self就指向nil了荆残,weakSelf也會被置為nil奴艾,等block回來時,其實在向一個nil發(fā)消息内斯。此時就用到了strongSelf蕴潦,它的作用主要是應對異步執(zhí)行的block,添加異步邏輯如下:

self.block = ^{  
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
        [NSThread sleepForTimeInterval:5];  
        [weakSelf print];  
    });  
};

此時的執(zhí)行結果如下:

2017-02-03 12:19:14.229 myBlock[6181:4566726] ------>dealloc

block中的語句沒有執(zhí)行俘闯,添加strongSelf邏輯之后:

self.block = ^{  
    __strong typeof(weakSelf) strongSelf = weakSelf;  
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
       [NSThread sleepForTimeInterval:5];  
        [strongSelf print]; 
   });  
};

5s之后的打印結果如下:

2017-02-03 13:30:23.129 myBlock[6327:4593186] ---->print  
2017-02-03 13:30:23.131 myBlock[6327:4593186] ------>dealloc

針對上面的過程潭苞,補充流程圖如下:

有人看到這里,肯定會有疑惑真朗,這里strongSelf的引用不又像前面的self一樣導致了循環(huán)引用了嗎此疹?這里需要好好解釋一下:
self是一個指向實例對象的指針,它的生命周期至少是伴隨著當前的實例對象的遮婶,所以一旦它和對象之間有循環(huán)引用是無法被自動打破的秀菱;strongSelf是block內(nèi)部的一個局部變量,變量的作用域僅限于局部代碼蹭睡,而程序一旦跳出作用域,strongSelf就會被釋放赶么,這個臨時產(chǎn)生的“循環(huán)引用”就會被自動打破肩豁,代碼的執(zhí)行事實上也是這樣子的。
我們可以簡單地驗證一下,首先我們用到一個CoreFoundation中的一個方法CFGetRetainCount清钥,它可以在ARC下獲取retainCount琼锋,修改main函數(shù)中如下:

@autoreleasepool  
{  
    MyTestBlock *testBlock = [[MyTestBlock alloc] init];  
    NSLog(@"block執(zhí)行前:%lu",CFGetRetainCount((__bridge CFTypeRef)testBlock));  
    testBlock.block();  
    NSLog(@"block執(zhí)行后:%lu",CFGetRetainCount((__bridge CFTypeRef)testBlock));  
}

執(zhí)行結果如下:

2017-02-03 13:49:34.465 myBlock[6380:4604974] block執(zhí)行前:1  
2017-02-03 13:49:34.466 myBlock[6380:4604974] block執(zhí)行后:2  
2017-02-03 13:49:39.469 myBlock[6380:4605000] ---->print  
2017-02-03 13:49:39.469 myBlock[6380:4605000] ------>dealloc
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市祟昭,隨后出現(xiàn)的幾起案子缕坎,更是在濱河造成了極大的恐慌,老刑警劉巖篡悟,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谜叹,死亡現(xiàn)場離奇詭異,居然都是意外死亡搬葬,警方通過查閱死者的電腦和手機荷腊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來急凰,“玉大人女仰,你說我怎么就攤上這事÷招猓” “怎么了疾忍?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長床三。 經(jīng)常有香客問我一罩,道長,這世上最難降的妖魔是什么勿璃? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任擒抛,我火速辦了婚禮,結果婚禮上补疑,老公的妹妹穿的比我還像新娘歧沪。我一直安慰自己,他們只是感情好莲组,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布诊胞。 她就那樣靜靜地躺著,像睡著了一般锹杈。 火紅的嫁衣襯著肌膚如雪撵孤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天竭望,我揣著相機與錄音邪码,去河邊找鬼。 笑死咬清,一個胖子當著我的面吹牛闭专,可吹牛的內(nèi)容都是我干的奴潘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼影钉,長吁一口氣:“原來是場噩夢啊……” “哼画髓!你這毒婦竟也來了?” 一聲冷哼從身側響起平委,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤奈虾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后廉赔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肉微,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年昂勉,在試婚紗的時候發(fā)現(xiàn)自己被綠了浪册。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡岗照,死狀恐怖村象,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情攒至,我是刑警寧澤厚者,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站迫吐,受9級特大地震影響库菲,放射性物質發(fā)生泄漏。R本人自食惡果不足惜志膀,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一熙宇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溉浙,春花似錦烫止、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惊奇,卻和暖如春互躬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背颂郎。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工吼渡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乓序。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓诞吱,卻偏偏與公主長得像舟奠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子房维,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容