Block總結

一、Block的底層結構及本質(zhì)

(1)block本質(zhì):

從代碼可以看出错英,Block的本質(zhì)就是NSObject. 也就是說block就是一個對象。

(2)block結構

利用clang將main.m文件編譯為cpp文件石抡〗两危看下block底層實現(xiàn)的結構



最終block是轉成了__main_block_impl_0的結構。



block結構體表示如下:

Block結構

二熬北、Block的變量capture機制

capture機制:為了保證block內(nèi)部能夠正常訪問外部的變量疙描。

1、局部變量捕獲:(會捕獲到block內(nèi)部成為新的結構體變量成員)

(1)auto類型的直接捕獲到block內(nèi)部(值傳遞)讶隐。

證明:


再看下impl結構體的變化


可以看到起胰,age值進過構造函數(shù)直接值傳遞到impl結構體里面的age。

(2)static 變量捕獲是指針傳遞

證明:


2巫延、全局變量捕獲

(1)全局變量在block里面是直接訪問效五。(不會捕獲到block內(nèi)部產(chǎn)生新的結構體變量成員)

證明:


從截圖可以看到,age在nslog打印時(block里面的任務被包裝成函數(shù))炉峰,是直接訪問拿來用的畏妖,并沒有捕獲進到block內(nèi)部。

(3)同理疼阔,全局的obj對象戒劫,也是不會捕獲到block里面,如果是全局NSObject*obj對象就直接訪問NSObject*obj婆廊,如果是static NSObject*obj對象迅细,就是會把NSObject**obj作為傳遞進去使用(&obj傳進去)。(二級指針)

三淘邻、Block的類型

Block類型有:__NSGlobalBlock__茵典、__NSStackBlock__、__NSMallocBlock__最終都是繼承自NSBlock類型宾舅,基類是NSObject统阿。(可查看最上面的截圖)

1枚尼、__NSGlobalBlock__(存放在數(shù)據(jù)區(qū)):沒有訪問auto變量的block就是__NSGlobalBlock__;

2砂吞、__NSStackBlock__(存放在棧段):訪問了auto變量的block就是__NSStackBlock__;

3崎溃、__NSMallocBlock__(存放堆段蜻直,需要程序猿管理):__NSStackBlock__調(diào)用copy后得到block就是__NSMallocBlock__;

(1)ARC環(huán)境下證明

(2)MRC環(huán)境下證明


如果block2賦值的時候加上copy袁串,則block會變成mallocBlock概而。

說明:

(1)ARC環(huán)境下,編譯器會根據(jù)情況自動將棧上的block復制到堆上囱修。比如:

? ? ? ? 1赎瑰、block作為函數(shù)返回值時

? ? ? ? 2、將block賦值給__strong指針時

? ? ? ? 3破镰、block作為方法名含有usingBlock的方法參數(shù)時

? ? ? ? 4餐曼、block作為GCD API的方法參數(shù)時

(2)ARC下block屬性的建議寫法:

? ? ? ? 1、@property(strong, nonatomic) void(^block)(void);

? ? ? ? 2鲜漩、@property(copy, nonatomic) void(^block)(void);

(3)MRC下block屬性的建議寫法:@property(copy, nonatomic) void(^block)(void);

四源譬、block內(nèi)部有對象類型的auto變量,內(nèi)存管理注意點

1孕似、當block內(nèi)部訪問了對象類型的auto變量時:

先看底層結構:

main函數(shù)代碼如下


轉cpp代碼:

結構體多了person指針

結構體desc里面多了兩個函數(shù)指針


copy與dispose函數(shù)的作用:對block捕獲的auto對象變量做內(nèi)存管理踩娘,retain/release等操作,具體如果操作根據(jù)auto對象變量修飾符的方式進行喉祭。

copy及dispose函數(shù)具體實現(xiàn)如下:


(1)如果block是在棧上养渴,將不會對auto變量產(chǎn)生強引用(or retain)

MRC證明:

ARC證明:


? (2)? 如果block被拷貝到堆上

1、會調(diào)用block內(nèi)部的copy函數(shù)

2泛烙、copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)

3理卑、_Block_object_assign函數(shù)會根據(jù)auto變量的修飾符(__strong、__weak胶惰、__unsafe_unretained)做出相應的操作傻工,形成強引用(retain)或者弱引用

4、如果block從堆上移除會調(diào)用block內(nèi)部的dispose函數(shù)孵滞,dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)中捆,_Block_object_dispose函數(shù)會自動釋放引用的auto變量(release)

說明:block捕獲__weak與__strong的auto變量對象,學到這里就需要知道為什么_strong的會導致強引用了坊饶。(默認不修飾auto對象變量泄伪,隱藏了修飾符__strong。)

五匿级、__block修飾符

可以用于解決block內(nèi)部無法修改auto變量值的問題蟋滴。

block捕獲進去的auto變量染厅,是不允許被修改的。因為block的實現(xiàn)內(nèi)容也是被包裝成一個函數(shù)的調(diào)用津函。函數(shù)與函數(shù)直接是不能跨域訪問auto變量的,auto變量屬于對應的函數(shù)椥ち福空間內(nèi)。

block可以修改全局變量跟static變量尔苦。

那我們來看看給auto變量加上__block后涩馆,為什么就可以修改變量了。

main函數(shù)的代碼如下:


轉cpp后允坚,看下__block int age的變化

從底層本質(zhì)看出魂那,不再是一個簡單的int age變量,而是經(jīng)過編譯器轉換成了對象(結構體__Block_byref_age_0)稠项,結構體里面存放著int age這個成員(從這大概可以知道涯雅,為了改變int age的值,先包裝成一個對象展运,利用結構體指針來修改成員int age值)活逆。

然后在block內(nèi)部修改age的值,是通過age結構體指針去修改結構體成員age的值拗胜。以此達到了我們修改age值的目的划乖。

特別說明:

1、__block int abc基本數(shù)據(jù)類型格式的捕獲

(1)如果block此時是在stack挤土,屬于stackBlock琴庵,此時block里面捕獲__block int abc變量,此時結構體(__Block_byref_abc_0)是在棧中仰美,當然結構體的age成員值也是在棧中迷殿。

證明:

? 特別說明:此處打印的abc地址是__Block_byref_abc_0結構體里面的成員int abc的地址。不是__Block_byref_abc_0結構體的地址咖杂。但是也可以說明__Block_byref_abc_0結構體目前是存在棧中庆寺。

(2)如果block進過copy,生成的mallocBlock,里面捕獲__block int abc變量诉字,此時__Block_byref_abc_0結構體也會被copy在堆上懦尝。

證明如下:

小tips: 因為有個全局的block1強引用著block,也就意味著在其他函數(shù)中也可以用的這個block,所以__block int age也必須copy到堆中才行壤圃,這樣才能保證全局block1(30)調(diào)用陵霉,可以修改abc的值。如果abc還在棧中伍绳,可能早就被其他數(shù)據(jù)覆蓋或者函數(shù)執(zhí)行完后成為了垃圾數(shù)據(jù)踊挠。

(3)struct__main_block_impl_0結構體里面的copy及dispose函數(shù)管理由__block int abc生轉換成的__Block_byref_abc_0的內(nèi)存。

編譯cpp后得到底層代碼如下:


管理結構體__Block_byref_abc_0內(nèi)存函數(shù)


2冲杀、__block NSObject*obj對象類型的捕獲

(1)同樣生成__Block_byref_obj_1的結構體

與之前的__block int age不同的是效床,這個結構體里面多了兩個函數(shù)copy,dispose.這個兩個函數(shù)用來對結構體里面的obj對象成員做內(nèi)存管理睹酌。

3、__block的內(nèi)存管理

(1)當block在棧上時剩檀,并不會對__block變量產(chǎn)生強引用

MRC環(huán)境證明:

ARC環(huán)境證明:


(2)當block被copy到堆時,會調(diào)用block內(nèi)部的copy函數(shù)(copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)),_Block_object_assign函數(shù)會對__block auto變量形成強引用(retain)憋沿,也會將__block auto變量拷貝到堆上。

ARC證明

(3)特殊請看沪猴,block被copy到堆時卤妒,捕獲的__block obj局部auto對象MRC情況下有點特別,不會被retain

請看下面代碼

內(nèi)存管理指示圖

(4)當block從堆中移除時,會調(diào)用block內(nèi)部的dispose函數(shù),dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù),_Block_object_dispose函數(shù)會自動釋放引用的__block變量(release)

(5)__block的__forwarding指針妙用:當我們的block復制到堆中時字币,__block變量也會復制到堆中,這時在棧中的__block變量和堆中的__block變量的__forwarding指針都會指向堆中的__block變量共缕。這樣做的目的:即使你修改棧中的block內(nèi)部的__block變量洗出,也能達到修改到堆中__block變量,防止程序修改出錯图谷。

六翩活、解決循環(huán)引用

1、ARC環(huán)境

第三種利用__block打破循環(huán)示意圖

2便贵、MRC環(huán)境

六菠镇、總結

1、Block本質(zhì)是OC對象承璃,基類也是NSObject

2利耍、Block分為:__NSGlobalBlock__、__NSStackBlock__盔粹、__NSMallocBlock__三種類型

3隘梨、Block捕獲auto變量(假設int age),會在Block結構體里面生成對應成員int age

4舷嗡、Block不會對全局變量(假設int tall)捕獲,在block里面直接可以訪問全局int tall轴猎,不會被捕獲

5、Block捕獲局部static變量(假設static int height),會在Block結構體里面生成對應成員int *height(指針)

6进萄、當block在棧上時捻脖,對(對象類型的auto變量、__block變量)都不會產(chǎn)生強引用

7中鼠、當block拷貝到堆上時可婶,都會通過copy函數(shù)來處理它們

? ? (1)__block變量(假設變量名叫做a) 調(diào)用如下函數(shù)

_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

? ? (2)對象類型的auto變量(假設變量名叫做obj) 調(diào)用如下函數(shù)

_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);

8、當block從堆上移除時援雇,都會通過dispose函數(shù)來釋放它們

(1)__block變量(假設變量名叫做a) 調(diào)用如下函數(shù)

_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

(2)對象類型的auto變量(假設變量名叫做obj)

_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);

9扰肌、當__block變量被copy到堆時,會調(diào)用__block變量內(nèi)部的copy函數(shù)熊杨,copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)曙旭,_Block_object_assign函數(shù)會根據(jù)所指向?qū)ο蟮男揎椃╛_strong盗舰、__weak、__unsafe_unretained)做出相應的操作桂躏,形成強引用(retain)或者弱引用(注意:這里僅限于ARC時會retain钻趋,MRC時不會retain)

10、如果__block變量從堆上移除會調(diào)用__block變量內(nèi)部的dispose函數(shù)剂习,dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)蛮位,_Block_object_dispose函數(shù)會自動釋放指向的對象(release)

11、ARC解決循環(huán)引用:(1)用__weak鳞绕、__unsafe_unretained解決(2)用__block解決(必須要調(diào)用block)

12失仁、MRC解決循環(huán)引用:(1)__unsafe_unretained (2)__block

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市们何,隨后出現(xiàn)的幾起案子萄焦,更是在濱河造成了極大的恐慌,老刑警劉巖冤竹,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拂封,死亡現(xiàn)場離奇詭異,居然都是意外死亡鹦蠕,警方通過查閱死者的電腦和手機冒签,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钟病,“玉大人萧恕,你說我怎么就攤上這事〕澹” “怎么了廊鸥?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辖所。 經(jīng)常有香客問我惰说,道長,這世上最難降的妖魔是什么缘回? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任吆视,我火速辦了婚禮,結果婚禮上酥宴,老公的妹妹穿的比我還像新娘啦吧。我一直安慰自己,他們只是感情好拙寡,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布授滓。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪般堆。 梳的紋絲不亂的頭發(fā)上在孝,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音淮摔,去河邊找鬼私沮。 笑死,一個胖子當著我的面吹牛和橙,可吹牛的內(nèi)容都是我干的仔燕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼魔招,長吁一口氣:“原來是場噩夢啊……” “哼晰搀!你這毒婦竟也來了?” 一聲冷哼從身側響起办斑,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤外恕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后俄周,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡髓迎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年峦朗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片排龄。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡波势,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橄维,到底是詐尸還是另有隱情尺铣,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布争舞,位于F島的核電站凛忿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏竞川。R本人自食惡果不足惜店溢,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望委乌。 院中可真熱鬧床牧,春花似錦、人聲如沸遭贸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至著蛙,卻和暖如春删铃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背册踩。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工泳姐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暂吉。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓胖秒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親慕的。 傳聞我的和親對象是個殘疾皇子阎肝,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

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

  • 要理解Block的實現(xiàn),要先理解runtime肮街,然而理解Runtime要先理解C語言的結構體(可見我基礎是TM有多...
    Kiven_Berry閱讀 455評論 0 0
  • 參考 Block編譯代碼解讀:block沒那么難(一风题、二、三)iOS進階——iOS(Objective-C) 內(nèi)存...
    啊哈呵閱讀 815評論 0 3
  • block的本質(zhì) block本質(zhì)上也是一個OC對象嫉父,它內(nèi)部也有個isa指針block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)...
    斑駁的流年無法釋懷閱讀 288評論 0 2
  • 上一篇文章iOS底層原理總結 - 探尋block的本質(zhì)(一)中已經(jīng)介紹過block的底層本質(zhì)實現(xiàn)以及了解了變量的捕...
    二斤寂寞閱讀 592評論 0 1
  • 推薦指數(shù): 6.0 書籍主旨關鍵詞:特權沛硅、焦點、注意力绕辖、語言聯(lián)想摇肌、情景聯(lián)想 觀點: 1.統(tǒng)計學現(xiàn)在叫數(shù)據(jù)分析,社會...
    Jenaral閱讀 5,721評論 0 5