【objc-zen-book】6.Block & self的循環(huán)引用

這個(gè)小系列是從 "Zen and the Art of the Objective-C Craftsmanship"中 進(jìn)行的摘抄斜做,共分成6篇箭启,大部分是講代碼風(fēng)格及美化,偶爾看看也不錯(cuò)。


Block

關(guān)于block在之前的文章中提到過(guò)芹务,這里就不多說(shuō)了择份。

一些關(guān)鍵點(diǎn):

block 是在棧上創(chuàng)建的

block 可以復(fù)制到堆上

Block會(huì)捕獲棧上的變量(或指針)狸吞,將其復(fù)制為自己私有的const(變量)胆描。

(如果在Block中修改Block塊外的)棧上的變量和指針,那么這些變量和指針必須用__block關(guān)鍵字申明(譯者注:否則就會(huì)跟上面的情況一樣只是捕獲他們的瞬時(shí)值)醋粟。

如果 block 沒(méi)有在其他地方被保持靡菇,那么它會(huì)隨著棧生存并且當(dāng)棧幀(stack frame)返回的時(shí)候消失。僅存在于棧上時(shí)米愿,block對(duì)對(duì)象訪(fǎng)問(wèn)的內(nèi)存管理和生命周期沒(méi)有任何影響厦凤。

如果 block 需要在棧幀返回的時(shí)候存在,它們需要明確地被復(fù)制到堆上育苟,這樣较鼓,block 會(huì)像其他 Cocoa 對(duì)象一樣增加引用計(jì)數(shù)。當(dāng)它們被復(fù)制的時(shí)候违柏,它會(huì)帶著它們的捕獲作用域一起博烂,retain 他們所有引用的對(duì)象。

如果一個(gè) block引用了一個(gè)棧變量或指針漱竖,那么這個(gè)block初始化的時(shí)候會(huì)擁有這個(gè)變量或指針的const副本脖母,所以(被捕獲之后再在棧中改變這個(gè)變量或指針的值)是不起作用的。(譯者注:所以這時(shí)候我們?cè)赽lock中對(duì)這種變量進(jìn)行賦值會(huì)編譯報(bào)錯(cuò):Variable is not assignable(missing __block type specifier)闲孤,因?yàn)樗麄兪歉北径沂莄onst的.具體見(jiàn)下面的例程)。

當(dāng)一個(gè) block 被復(fù)制后烤礁,__block 聲明的棧變量的引用被復(fù)制到了堆里讼积,復(fù)制完成之后,無(wú)論是棧上的block還是剛剛產(chǎn)生在堆上的block(棧上block的副本)都會(huì)引用該變量在堆上的副本脚仔。

   ...
   CGFloat blockInt = 10;
   void (^playblock)(void) = ^{
        NSLog(@"blockInt = %zd", blockInt);
    };
    blockInt ++;
    playblock();
    ...

    //結(jié)果為:blockInt = 10

self的循環(huán)引用

當(dāng)使用代碼塊和異步分發(fā)的時(shí)候勤众,要注意避免引用循環(huán)。

// 這樣做
__weak __typeof(self) weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
    [weakSelf doSomethingWithData:data];
}];

// 不要這樣
[self executeBlock:^(NSData *data, NSError *error) {
    [self doSomethingWithData:data];
}];

多個(gè)語(yǔ)句的例子:

// 這樣做
__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doSomethingWithData:data];
        [strongSelf doSomethingWithData:data];
    }
}];

// 不要這樣
__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
    [weakSelf doSomethingWithData:data];
    [weakSelf doSomethingWithData:data];
}];

你應(yīng)該把這兩行代碼作為 snippet 加到 Xcode 里面并且總是這樣使用它們鲤脏。

__weak __typeof(self)weakSelf = self;
__strong __typeof(weakSelf)strongSelf = weakSelf;

在使用block的時(shí)候们颜,分三種情況:

  • 直接在 block 里面使用關(guān)鍵詞 self

    在這種情況下吕朵,首先要看這個(gè)block是什么樣的。

    dispatch_block_t completionBlock = ^{
        NSLog(@"%@", self);
    }
    
    MyViewController *myController = [[MyViewController alloc] init...];
    [self presentViewController:myController
                       animated:YES
                     completion:completionHandler];
    

    在這種情況下窥突,在block中直接使用self并沒(méi)有問(wèn)題努溃。因?yàn)樵赽lock中對(duì)self進(jìn)行引用,但是self并沒(méi)有保留這個(gè)block阻问。

    但是如果是下面這種:

    self.completionHandler = ^{
        NSLog(@"%@", self);
    }
    
    MyViewController *myController = [[MyViewController alloc] init...];
    [self presentViewController:myController
                       animated:YES
                     completion:self.completionHandler];
    

    在block里面引用了self梧税,而這個(gè)block也被self保留(這個(gè)block是一個(gè)屬性),那么會(huì)造成引用循環(huán)称近。解決方法就是使用__weak第队。

  • 在 block 外定義一個(gè) __weak 的 引用到 self,并且在 block 里面使用這個(gè)弱引用刨秆。

    這樣會(huì)避免循壞引用凳谦,也是通常情況下我們的block作為類(lèi)的屬性被self retain 的時(shí)候會(huì)做的。

    __weak typeof(self) weakSelf = self;
    self.completionHandler = ^{
        NSLog(@"%@", weakSelf);
    };
    
    MyViewController *myController = [[MyViewController alloc] init...];
    [self presentViewController:myController
                       animated:YES
                     completion:self.completionHandler];
    

    這個(gè)情況下self在屬性里面 retain 了 block衡未,但是block 沒(méi)有 retain self尸执。所以這樣我們能保證了安全的訪(fǎng)問(wèn) self。

  • 在 block 外定義一個(gè) __weak 的 引用到 self眠屎,并在在 block 內(nèi)部通過(guò)這個(gè)弱引用定義一個(gè) __strong 的引用剔交。

    和并發(fā)執(zhí)行有關(guān)。當(dāng)涉及異步的服務(wù)的時(shí)候改衩,block 可以在之后被執(zhí)行岖常,并且不會(huì)發(fā)生關(guān)于 self 是否存在的問(wèn)題。(關(guān)于這個(gè)我目前為止并不是很明白葫督,希望有明白的朋友多多指教竭鞍。)


所有文章
【objc-zen-book】1.條件語(yǔ)句&Case語(yǔ)句的注意
【objc-zen-book】2.命名
【objc-zen-book】3.類(lèi)
【objc-zen-book】4.Category & NSNotification
【objc-zen-book】5.美化代碼 & 代碼組織
【objc-zen-book】6.Block & self的循環(huán)引用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市橄镜,隨后出現(xiàn)的幾起案子偎快,更是在濱河造成了極大的恐慌,老刑警劉巖洽胶,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晒夹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡姊氓,警方通過(guò)查閱死者的電腦和手機(jī)丐怯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)翔横,“玉大人读跷,你說(shuō)我怎么就攤上這事『萄洌” “怎么了效览?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵无切,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我丐枉,道長(zhǎng)哆键,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任矛洞,我火速辦了婚禮洼哎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沼本。我一直安慰自己噩峦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布抽兆。 她就那樣靜靜地躺著识补,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辫红。 梳的紋絲不亂的頭發(fā)上凭涂,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音贴妻,去河邊找鬼切油。 笑死,一個(gè)胖子當(dāng)著我的面吹牛名惩,可吹牛的內(nèi)容都是我干的澎胡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼娩鹉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼攻谁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起弯予,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤戚宦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后锈嫩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體受楼,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年呼寸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了那槽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡等舔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糟趾,到底是詐尸還是另有隱情慌植,我是刑警寧澤甚牲,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站蝶柿,受9級(jí)特大地震影響丈钙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜交汤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一雏赦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芙扎,春花似錦星岗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至圈浇,卻和暖如春寥掐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背磷蜀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工召耘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人褐隆。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓污它,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親妓灌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子轨蛤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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