iOS __weak和__strong在Block中的使用

1. __weak使用

1.1 ARC以后引入了__weak的概念來修飾Objective-C對象,使用這個關鍵字修飾的對象,對象的引用計數不會+1,這個關鍵字和__unsafe_unretained有些類似,只是在對象釋放的時候__weak會將引用的對象置為nil,而__unsafe_unretained不會,這將會導致野指針的產生,所以一般情況下,我們一般不屬于強引用某個對象的時候,可以使用__weak進行修飾,典型的例子就是代理.例如

class UICollectionView.h
@property (nonatomic, weak, nullable) id <UICollectionViewDelegate> delegate;

1.2 一個對象在聲明的時候,如果什么修飾符都不寫,默認是strong,就會導致引用計數加+1,當出了這個對象聲明的scope,引用計數就會-1,如果引用計數為0,這個對象就會被釋放.
當一個強引用的對象被Block捕獲的時候,引用計數就會增加,當這個只有當這個Block被釋放的時候這個強引用的對象的計數器才會回到原先的大小.
例如

void (^block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        {
            TestObj *obj = [[TestObj alloc] init];
            NSLog(@"before block retainCount:%zd",[obj arcDebugRetainCount]);
            block = ^(){
                NSLog(@"TestObj對象地址:%@",obj);
            };
            NSLog(@"after block retainCount:%zd",[obj arcDebugRetainCount]);
        }
        block();
    }
    return 0;
}

打印的結果為:

DemoWeak[19005:6761339] before block retainCount:1
DemoWeak[19005:6761339] after block retainCount:3
DemoWeak[19005:6761339] TestObj對象地址:<TestObj: 0x602000006b30>

比較有意思的是引用計數并不是簡單的+1,而是加2,這是由于block在創建的時候在棧上,而在賦值給全局變量的時候,被拷貝到了堆上,證明如下:

NSLog(@"堆%@",[block class]);
NSLog(@"棧%@",[^(){NSLog(@"TestObj對象地址:%@",obj);} class]);
DemoWeak[19026:6763489] 堆__NSMallocBlock__
DemoWeak[19026:6763489] 棧__NSStackBlock__

由于Block對對象的強引用,導致如果這個Block一直不釋放,那么所強引用的對象也就無法釋放,這樣就會導致對象的dealloc方法無法執行,以前就遇到了這種對象不釋放,但仍然發送通知的情況,找了好久,解決這個問題也很簡單,我們只需要將Block將要強引用的對象,弱引用就可以了,代碼如下

void (^block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        {
            TestObj *obj = [[TestObj alloc] init];
            __weak TestObj *weakObj = obj;
            NSLog(@"before block retainCount:%zd",[obj arcDebugRetainCount]);
            block = ^(){
                NSLog(@"TestObj對象地址:%@",weakObj);
            };
            NSLog(@"after block retainCount:%zd",[obj arcDebugRetainCount]);
        }
        block();
    }
    return 0;
}

打印結果如下

DemoWeak[19065:6774290] before block retainCount:1
DemoWeak[19065:6774290] after block retainCount:1

可以看到弱引用前后,對象的計數器并沒有什么改變.

剛開始學習Block的時候,總是覺得由于Block只是定義,而并沒有執行,所以想當然的以為Block并沒有對內部的引用對象有任何影響,知道看到了Block編譯后的代碼,下面就是編譯后,截取的部分代碼


編譯后.png

從圖片上可以看出block在聲明的時候,就已經調用了初始化函數,保存了Block所捕獲到的引用對象(注意,由于使用__weak,無法編譯出.cpp源文件,所以在編譯時候,我使用了__unsafe_unretained,除了上面說到他和__weak之間的區別,實驗的結果都是一樣的)

2. __strong使用

除了在Block外使用__weak對對象進行弱引用,我們偶爾還需要在Block內部對弱引用對象進行一次強引用,這是由于, 僅用__weak所修飾的對象,如果被釋放,那么這個對象在Block執行的過程中就會變成nil,這就可能會帶來一些問題,比如,數組,字典的插入.
正確的做法是,在Block執行的開始,檢驗弱引用的對象是否還存在,如果還存在,使用__strong進行強引用,這樣,在Block執行的過程中,這個對象就不會被置為nil,而在Block執行完畢后,對象的引用計數就會-1,這樣就不會導致對象無法釋放.

Block從外界所捕獲的對象和在Block內部強使用__strong強引用的對象,差別就在于一個是在定義的時候就會影響對象的引用計數(理由就是上面編譯后的代碼),一個是在Block運行的時候才強引用對象,執行完畢還是會-1

一般情況下,只使用__weak就能滿足大部分的需求了,只有在多線程處理的時候,需要在Block使用下__strong修飾對象,因為,單個線程,要么執行Block的時候對象還沒有被置為nil,那么直到Block被執行完畢,這個對象都不會被釋放(釋放也是需要線程調用函數的不是?),但是在多線程的情況下,就可能造成,在執行上半部分代碼的時候,對象還在,而在執行下半部代碼的時候對象已經被釋放,下面是一個例子:

TestObj *obj = [[TestObj alloc] init];
__weak TestObj *weakObj = obj;
NSLog(@"before block retainCount:%zd",[obj arcDebugRetainCount]);
block = ^(){
    NSLog(@"TestObj對象地址:%@",weakObj);
    dispatch_async(dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
        
        for (int i = 0; i < 1000000; i++) {
            // 模擬一個耗時的任務
        }

        NSLog(@"耗時的任務 結束 TestObj對象地址:%@",weakObj);
    });
};
NSLog(@"after block retainCount:%zd",[obj arcDebugRetainCount]);
block();

打印結果:

DemoWeek[19247:6816518] before block retainCount:1
DemoWeek[19247:6816518] after block retainCount:1
DemoWeek[19247:6816518] TestObj對象地址:<TestObj: 0x602000006af0>
DemoWeek[19247:6816518] TestObj 對象已釋放
DemoWeek[19247:6816544] 耗時的任務 結束 TestObj對象地址:(null)

可以看到在耗時任務執行前對象還是存在的,只是在執行完畢了后,對象被釋放了,如果我們使用__strong修飾就可以避免這種情況

block = ^(){
    __strong  TestObj *strongObj = weakObj;
        if(! strongObj) return;
    NSLog(@"TestObj對象地址:%@",strongObj);
    dispatch_async(dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
        
        for (int i = 0; i < 1000000; i++) {
            // 模擬一個耗時的任務
        }

        NSLog(@"耗時的任務 結束 TestObj對象地址:%@",strongObj);
    });

打印結果:

DemoWeek[19280:6819437] before block retainCount:1
DemoWeek[19280:6819437] after block retainCount:1
DemoWeek[19280:6819437] TestObj對象地址:<TestObj: 0x602000006b30>
DemoWeek[19280:6819464] 耗時的任務 結束 TestObj對象地址:<TestObj: 0x602000006b30>
DemoWeek[19280:6819464] TestObj 對象已釋放

總結:

__weak修飾的對象被Block引用,不會影響對象的釋放,而__strong在Block內部修飾的對象,會保證,在使用這個對象在scope內,這個對象都不會被釋放,出了scope,引用計數就會-1,且__strong主要是用在多線程運用中,若果只使用單線程,只需要使用__weak即可

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容