FBRetainCycleDetector中獲取block強(qiáng)引用的對(duì)象實(shí)現(xiàn)方式

FBRetainCycleDetector中獲取block強(qiáng)引用的對(duì)象實(shí)現(xiàn)方式

在我的上一篇文章中介紹了如何獲取block捕獲的對(duì)象庙洼,思路是通過解析block內(nèi)部的layout簽名串顿痪。最近看FBRetainCycleDetector源碼時(shí)發(fā)現(xiàn)它用了一種十分巧妙的方式獲取,第一見到時(shí)我也被這種新奇的方式驚艷到油够,下面就開始正文看下它是如何做的蚁袭。

獲取block強(qiáng)引用的相關(guān)代碼在FBBlockStrongLayout.m中的static NSIndexSet *_GetBlockStrongLayout(void *block)函數(shù)中,如下所示:

static NSIndexSet *_GetBlockStrongLayout(void *block) {
  struct BlockLiteral *blockLiteral = block;

  /**
   BLOCK_HAS_CTOR - Block has a C++ constructor/destructor, which gives us a good chance it retains
   objects that are not pointer aligned, so omit them.

   !BLOCK_HAS_COPY_DISPOSE - Block doesn't have a dispose function, so it does not retain objects and
   we are not able to blackbox it.
   */
  if ((blockLiteral->flags & BLOCK_HAS_CTOR)
      || !(blockLiteral->flags & BLOCK_HAS_COPY_DISPOSE)) {
    return nil;
  }

  void (*dispose_helper)(void *src) = blockLiteral->descriptor->dispose_helper;
  const size_t ptrSize = sizeof(void *);

  // 計(jì)算一個(gè)block的size能夠存放多少指針大小的數(shù)據(jù)石咬,向上取整
  const size_t elements = (blockLiteral->descriptor->size + ptrSize - 1) / ptrSize;

  // 通過數(shù)組構(gòu)造一個(gè)虛擬block
  void *obj[elements];
  //保存detector對(duì)象
  void *detectors[elements];

  for (size_t i = 0; i < elements; ++i) {
    FBBlockStrongRelationDetector *detector = [FBBlockStrongRelationDetector new];
    obj[i] = detectors[i] = detector;
  }

  @autoreleasepool {
  //調(diào)用block的析構(gòu)函數(shù)揩悄,這個(gè)obj就是手動(dòng)構(gòu)造的虛擬block
    dispose_helper(obj);
  }

  // Run through the release detectors and add each one that got released to the object's
  // strong ivar layout.
  NSMutableIndexSet *layout = [NSMutableIndexSet indexSet];

  for (size_t i = 0; i < elements; ++i) {
    FBBlockStrongRelationDetector *detector = (FBBlockStrongRelationDetector *)(detectors[i]);
    if (detector.isStrong) {
      [layout addIndex:i];
    }

    // Destroy detectors
    [detector trueRelease];
  }

  return layout;
}

首先是將block強(qiáng)轉(zhuǎn)成struct BlockLiteral類型的block底層結(jié)構(gòu),然后通過BLOCK_HAS_COPY_DISPOSE這個(gè)標(biāo)識(shí)位查看block是否有析構(gòu)函數(shù)鬼悠,如果沒有則代表block沒有捕獲對(duì)象直接返回nil删性。如果有析構(gòu)函數(shù)則獲取到block中的析構(gòu)函數(shù)指針,這個(gè)析構(gòu)函數(shù)的作用主要就是對(duì)block中捕獲的強(qiáng)引用對(duì)象調(diào)用release方法焕窝。然后后面獲取到block的size镇匀,并計(jì)算這個(gè)size能存放多少個(gè)指針大小的數(shù)據(jù),64位系統(tǒng)下也即是size/8向上取整的結(jié)果袜啃。后面就到了整個(gè)實(shí)現(xiàn)最巧妙的地方了,首先是創(chuàng)建了兩個(gè)數(shù)組汗侵,obj和detectors,這兩個(gè)數(shù)組的大小和block的大小是一致的群发,然后用FBBlockStrongRelationDetector對(duì)象填充這兩個(gè)數(shù)組晰韵,然后調(diào)用block的析構(gòu)函數(shù)dispose_helper(obj),可以看到這里是將obj數(shù)組傳進(jìn)去了熟妓,所以這里obj就是一個(gè)手動(dòng)構(gòu)造的虛擬block,我們先來假設(shè)有這這樣一段代碼

    NSObject *obj_strong1 = [NSObject new];
    NSObject *obj_strong2 = [NSObject new];
    int aVal = 10;
    int bVal = 20;
    
    void (^aBlock)(void) = ^{
        [obj_strong1 description];
        [obj_strong2 description];
        int c = aVal + bVal;
    };

可以看出aBlock是捕獲了兩個(gè)強(qiáng)引用對(duì)象和兩個(gè)int類型變量雪猪。那么通過數(shù)組構(gòu)造的那個(gè)虛擬block如下所示

圖1

我說過dispose_helper方法是對(duì)block捕獲的對(duì)象調(diào)用release方法,所以上圖中紅框圈出的部分會(huì)調(diào)用release起愈,也即是[detctor release];detctor是FBBlockStrongRelationDetector類型只恨,我們來看看這個(gè)類的實(shí)現(xiàn)。

圖2

注意到它重寫了release方法抬虽,并且在release調(diào)用的時(shí)候?qū)strong設(shè)置為YES官觅,也就是說dispose_helper(obj)調(diào)用的時(shí)候圖1中index為4,5的detector對(duì)象會(huì)進(jìn)入到release方法并標(biāo)記為strong阐污。

如此就找到了強(qiáng)引用對(duì)象休涤,后面就是遍歷detectors數(shù)組,然后將strong == YES的對(duì)象篩選出來并保存。最后因?yàn)橹貙懥藃elease方法功氨,對(duì)象并沒有真正釋放序苏,還需要調(diào)用trueRelease進(jìn)行收尾工作。

不得不說這是一個(gè)非常巧妙的方法捷凄,不僅需要對(duì)block的底層結(jié)構(gòu)非常了解忱详,還需要對(duì)內(nèi)存分布有著充分了解。但是這種方法仍然有著局限性跺涤,例如 __block id obj = [NSObject new];這種通過__block修飾的對(duì)象并不能獲取匈睁,因?yàn)開_block修飾的變量底層轉(zhuǎn)成了byref的結(jié)構(gòu)體變量,雖然其也有isa指針钦铁,但是實(shí)際它的isa是設(shè)置為0软舌,它并不是一個(gè)NSObject類型的對(duì)象,也就不會(huì)調(diào)用到release方法牛曹。但在實(shí)際開發(fā)中這種__block id obj形式非常少見佛点,所以也無傷大雅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末黎比,一起剝皮案震驚了整個(gè)濱河市超营,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阅虫,老刑警劉巖演闭,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異颓帝,居然都是意外死亡米碰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門购城,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吕座,“玉大人,你說我怎么就攤上這事瘪板∥馀浚” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵侮攀,是天一觀的道長(zhǎng)锣枝。 經(jīng)常有香客問我,道長(zhǎng)兰英,這世上最難降的妖魔是什么撇叁? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮箭昵,結(jié)果婚禮上税朴,老公的妹妹穿的比我還像新娘。我一直安慰自己家制,他們只是感情好正林,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颤殴,像睡著了一般觅廓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涵但,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天杈绸,我揣著相機(jī)與錄音,去河邊找鬼矮瘟。 笑死瞳脓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的澈侠。 我是一名探鬼主播劫侧,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼哨啃!你這毒婦竟也來了烧栋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拳球,失蹤者是張志新(化名)和其女友劉穎审姓,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祝峻,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡魔吐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莱找。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酬姆。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宋距,靈堂內(nèi)的尸體忽然破棺而出轴踱,到底是詐尸還是另有隱情,我是刑警寧澤谚赎,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布淫僻,位于F島的核電站,受9級(jí)特大地震影響壶唤,放射性物質(zhì)發(fā)生泄漏雳灵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一闸盔、第九天 我趴在偏房一處隱蔽的房頂上張望悯辙。 院中可真熱鬧,春花似錦、人聲如沸躲撰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拢蛋。三九已至桦他,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谆棱,已是汗流浹背快压。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垃瞧,地道東北人蔫劣。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像个从,于是被迫代替她去往敵國(guó)和親脉幢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355