iOS - 深入探究Block循環(huán)引用

作者:Mitchell 

一愿险、根據(jù)需求提出問題

  • 請耐心把這篇文章看完颇蜡,你對 Block 會有更深刻的了解。
  • 這里直接用一個需求來探究循環(huán)引用的問題:如果我想在Block中延時來運行某段代碼辆亏,這里就會出現(xiàn)一個問題风秤,看這段代碼:
 - (void)viewDidLoad {
    [super viewDidLoad];
    MitPerson*person = [[MitPerson alloc]init];
    __weak MitPerson * weakPerson = person;
    person.mitBlock = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [weakPerson test];
        });
    };
    person.mitBlock();
}

直接運行這段代碼會發(fā)現(xiàn)[weakPerson test];并沒有執(zhí)行,打印一下會發(fā)現(xiàn)扮叨,weakPerson 已經(jīng)是 Nil 了缤弦,這是由于當我們的 viewDidLoad 方法運行結束,由于是局部變量彻磁,無論是 MitPerson 和 weakPerson 都會被釋放掉碍沐,那么這個時候在 Block 中就無法拿到正真的 person 內容了。

  • 按如下方法修改代碼:
 - (void)viewDidLoad {
    [super viewDidLoad];
    MitPerson*person = [[MitPerson alloc]init];
    __weak MitPerson * weakPerson = person;
    person.mitBlock = ^{
        __strong MitPerson * strongPerson = weakPerson;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [strongPerson test];
        });
    };
    person.mitBlock();
}

這樣當2秒過后衷蜓,計時器依然能夠拿到想要的 person 對象累提。

二、深入探究原理

  • 這里將會對每行代碼逐步進行說明
1磁浇、開辟一段控件存儲 person 類對象內容斋陪,創(chuàng)建 person 強指針。
 MitPerson*person = [[MitPerson alloc]init];
2置吓、創(chuàng)建一個弱指針 weakPerson 指向person對象內容 
 __weak MitPerson * weakPerson = person;
  person.mitBlock = ^{
  3无虚、在 person 對象的 Block 內部創(chuàng)建一個強指針來指向 person 對象,為了保證當計時器執(zhí)行代碼的時候衍锚,person 對象沒有被系統(tǒng)銷毀所以我們必須在系統(tǒng)內部進行一次強引用友题,并用 GCD 計時器引用 strongPerson,為了保留 person 對象戴质,在下面會對這里更加詳細的說明度宦。
    __strong MitPerson * strongPerson = weakPerson;
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          [strongPerson test];
      });
  };
4、執(zhí)行 Block 代碼
    person.mitBlock();
  • 下面將詳細分析一下下面這段代碼:
  person.mitBlock = ^{
 __strong MitPerson * strongPerson = weakPerson;
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          [strongPerson test];
      });
  };
  • 首先需要明白一些關于 Block 的概念:
    • 1置森、默認情況下斗埂,block 是放在棧里面的
    • 2、一旦blcok進行了copy操作凫海,block的內存就會被放在堆里面
    • 3呛凶、堆立面的block(被copy過的block)有以下現(xiàn)象
      • 1> block內部如果通過外面聲明的強引用來使用,那么block內部會自動產(chǎn)生一個強引用指向所使用的對象行贪。
      • 2> block內部如果通過外面聲明的弱引用來使用漾稀,那么block內部會自動產(chǎn)生一個弱引用指向所使用的對象模闲。
  • 我們進行這段代碼的目的:
    • 首先,我們需要在 Block 塊中調用崭捍,person 對象的方法尸折,既然是在 Block 塊中我們就應該使用弱指針來引用外部變量,以此來避免循環(huán)引用殷蛇。但是又會出現(xiàn)問題实夹,什么問題呢?就是當我計時器要執(zhí)行方法的時候粒梦,發(fā)現(xiàn)對象已經(jīng)被釋放了亮航。
    • 接下來就是為了避免 person 對象在計時器執(zhí)行的時候被釋放掉:那么為什么 person 對象會被釋放掉呢?因為無論我們的person強指針還是 weakPerson 弱指針都是局部變量匀们,當執(zhí)行完ViewDidLoad 的時候缴淋,指針會被銷毀。對象只有被強指針引用的時候才不會被銷毀泄朴,而我們如果直接引用外部的強指針對象又會產(chǎn)生循環(huán)引用重抖,這個時候我們就用了一個巧妙的代碼來完成這個需求。
    • 首先在 person.mitBlock 引用外部 weakPerson祖灰,并在內部創(chuàng)建一個強指針去指向 person 對象钟沛,因為在內部聲明變量,Block 是不會強引用這個對象的局扶,這也就在避免的 person.mitBlock 循環(huán)引用風險的同時讹剔,又創(chuàng)建出了一個強指針指向對象。
    • 之后再用 GCD 延時器 Block 來引用相對于它來說是外部的變量 strongPerson 详民,這時延時器 Block 會默認創(chuàng)建出來一個強引用來引用 person 對象,當 person.mitBlock 作用域結束之后 strongPerson 會跟著被銷毀陌兑,內存中就僅剩下了 延時器 Block 強引用著 person 對象沈跨,2秒之后觸發(fā) test 方法,GCD Block 內部方法執(zhí)行完畢之后兔综,延時器和對象都被銷毀饿凛,這樣就完美實現(xiàn)了我們的需求。
  • 最后再用一張圖來闡述各個指針软驰、Block 與對象之間的關系
    黑色代表強引用涧窒,綠色代表弱引用
    • 總結:person.mitBlock 中創(chuàng)建 strongPerson 是為了能夠使 GCD Block 保存 person 對象,創(chuàng)建 strongPerson 時候使用 weakPerson 是為了避免 mitBlock 直接引用外部強指針變量所造成的循環(huán)引用锭亏。
      Block循環(huán)引用.png

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末纠吴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子慧瘤,更是在濱河造成了極大的恐慌戴已,老刑警劉巖固该,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異糖儡,居然都是意外死亡伐坏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門握联,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桦沉,“玉大人,你說我怎么就攤上這事金闽〈柯叮” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵呐矾,是天一觀的道長苔埋。 經(jīng)常有香客問我,道長蜒犯,這世上最難降的妖魔是什么组橄? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮罚随,結果婚禮上玉工,老公的妹妹穿的比我還像新娘。我一直安慰自己淘菩,他們只是感情好遵班,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著潮改,像睡著了一般狭郑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汇在,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天翰萨,我揣著相機與錄音,去河邊找鬼糕殉。 笑死亩鬼,一個胖子當著我的面吹牛,可吹牛的內容都是我干的阿蝶。 我是一名探鬼主播雳锋,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼羡洁!你這毒婦竟也來了玷过?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冶匹,沒想到半個月后习劫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡嚼隘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年诽里,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飞蛹。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡谤狡,死狀恐怖,靈堂內的尸體忽然破棺而出卧檐,到底是詐尸還是另有隱情墓懂,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布霉囚,位于F島的核電站捕仔,受9級特大地震影響,放射性物質發(fā)生泄漏盈罐。R本人自食惡果不足惜榜跌,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盅粪。 院中可真熱鬧钓葫,春花似錦、人聲如沸票顾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奠骄。三九已至豆同,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間含鳞,已是汗流浹背诱告。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留民晒,地道東北人。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓锄禽,卻偏偏與公主長得像潜必,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子沃但,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內容

  • 《Objective-C高級編程》這本書就講了三個東西:自動引用計數(shù)磁滚、block、GCD,偏向于從原理上對這些內容...
    WeiHing閱讀 9,804評論 10 69
  • iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對C語言的擴展,用來實現(xiàn)匿名函數(shù)的特性,B...
    smile刺客閱讀 2,335評論 2 26
  • Block使用場景垂攘,可以在兩個界面的傳值维雇,也可以對代碼封裝作為參數(shù)的傳遞等。用過GCD就知道Block的精妙之處晒他。...
    Coder_JMicheal閱讀 720評論 2 1
  • 請耐心把這篇文章看完吱型,你對 Block 會有更深刻的了解。 這里直接用一個需求來探究循環(huán)引用的問題:如果我想在Bl...
    平凡的心閱讀 732評論 1 10
  • 前言 Blocks是C語言的擴充功能陨仅,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,759評論 0 23