Block探究:第二篇(Stack_Block)

原文地址:http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/
如原作者發(fā)現(xiàn)有侵權(quán)行為可責(zé)令我在24小時之內(nèi)刪除婚脱,前提是你能看到。

這是我從編譯器角度了解block內(nèi)部實(shí)現(xiàn)的第二篇文章。在這篇文章里障贸,我將會研究一下block在棧上的幾種類型错森。


Block類型

第一篇文章里我們了解到,block中有一個isa指針指向了_NSConcreteGlobalBlock這個類篮洁,因為block結(jié)構(gòu)體和block中的descriptor結(jié)構(gòu)體在編譯時就全部初始化完成了涩维,所以我們可以看到里面的變量。block有幾種不同的類型嘀粱,每種類型都有與其關(guān)聯(lián)的類激挪,但是我們只需要考慮他們中常見的三種即可:
1._NSConcreteGlobalBlock是一種在編譯期間就完全定義好的全局block,這類block沒有捕獲任何外部變量锋叨,例如一個空的block垄分。
2._NSConcreteStackBlock是一種位于棧上的block,這種block在最終被copy到堆中之前一直都存儲在棧上薄湿。
3._NSConcreteMallocBlock是一種位于堆上的block,在對block進(jìn)行copy操作之后听诸,它的引用計數(shù)會增加坐求,知道引用計數(shù)減為0晌梨,這個block就會被釋放桥嗤。


有捕獲范圍的block

來看看下面的代碼:

block捕獲外部變量

名為foo的函數(shù)有一個參數(shù),在block中調(diào)用foo函數(shù)的時候?qū)⑼饷娴淖兞?em>a捕獲并傳給函數(shù)仔蝌,同樣的渊鞋,我們來看看在armv7架構(gòu)下匯編的過程锡宋,相關(guān)代碼如下:

block捕獲外部變量匯編

這段匯編代碼和第一篇中的一樣奠滑,調(diào)用了block的invoke函數(shù)摊崭,接下來看看doBlockA函數(shù):
doBlockA函數(shù)

這段和之前的比起來有點(diǎn)難度了,和之前的加載全局block不同,這里做了更多的事根时,雖然看起來有點(diǎn)恐怖含友,但認(rèn)真看其實(shí)很容易看到它做了些什么窘问。因為編譯器沒有對這些指令進(jìn)行優(yōu)化惠赫,所以我將要對這些代碼在不改變原有功能的基礎(chǔ)上重新整理一下把鉴,下面是整理后的代碼:

整理后的doBlockA函數(shù)

主要的功能點(diǎn)如下:
1.一開始讓r7入棧是為了避免r7的內(nèi)容被覆蓋峰搪,因為r7里面的內(nèi)容在函數(shù)間調(diào)用時需要用到。lr(鏈接寄存器)保存了函數(shù)返回時要執(zhí)行的下一條指令的地址。最后將棧指針保存在r7中奉呛。
2.從棧頂指針減去24個字節(jié),也就是從棧中開辟出24字節(jié)用來存儲數(shù)據(jù)。
3.這里的代碼是為了對符號L__NSConcreteStackBlock$non_lazy_ptr進(jìn)行尋址,跟pc有關(guān),當(dāng)代碼最終被鏈接時不管這段代碼在二進(jìn)制文件中的什么位置計算機(jī)都能通過pc找到它半等,并將它存儲在棧指針?biāo)傅膬?nèi)存地址中杀饵。
4.值1073741824被存儲在棧頂指針+4的位置谜悟。
5.將0存儲到棧指針 + 8 的位置∥颠叮現(xiàn)在蔑水,將要發(fā)生什么可能已經(jīng)變得逐漸清晰了——在棧上創(chuàng)建了一個Block_layout結(jié)構(gòu)歇父!到現(xiàn)在為止,已經(jīng)設(shè)置了該結(jié)構(gòu)的3個值:isa指針,flagsreserved值肺樟。
6.___doBlockA_block_invoke_0存儲在棧指針+12的地址處,也就是block結(jié)構(gòu)體的invoke屬性。
7.___block_descriptor_tmp存儲在棧指針+16的地址處擎鸠,也就是block結(jié)構(gòu)體的descriptor屬性袜蚕。
8.值128存儲在棧指針+20的地址處雄可,你可能發(fā)現(xiàn)了Block_layout結(jié)構(gòu)體中只有5個值,并且這5個值已經(jīng)都存儲在棧中了室梅,那么接下來棧中要存儲什么敷待?你會發(fā)現(xiàn)正是從外面捕獲到的值128。所以這肯定就是存儲block使用到的值的地方——在Block_layout結(jié)構(gòu)尾部迅矛。
9.現(xiàn)在棧指針指向的是一個完整的block機(jī)構(gòu)體,然后將棧指針存入r0中悴势,然后調(diào)用runBlockA函數(shù)。(注意:在ARM EABI中r0通常用來存放函數(shù)的第一個參數(shù))粪躬。
10.最后笨腥,讓棧指針+24收回之前在棧中開辟的24字節(jié)的空間士鸥,接著將棧中的內(nèi)容分別pop到r7pc中玻侥,pop回r7的就是函數(shù)一開始從r7push到棧中的內(nèi)容,pc的值是函數(shù)開始時push到棧中的lr的值音半,這樣在函數(shù)返回時程序可以繼續(xù)執(zhí)行下面的指令斥铺。

哇洼哎,如果你看到這的話抽兆,說明你太棒了壕探!

最后一部分讓俺們來看看block中的invoke函數(shù)和descriptor編譯的結(jié)果。我希望他們要比第一篇中的global block的這兩個屬性簡單郊丛±钋耄看代碼:

invoke和descriptor

的確和第一篇中的沒有什么差別,唯一不同的是descriptor中的size值的大小不同厉熟,現(xiàn)在是24而不是20导盅,這是因為block捕獲到一個整數(shù)所以block的大小變?yōu)榱?4,之前也看到在block創(chuàng)建的時候有額外的4個字節(jié)在block的尾部一起被存入了棧中揍瑟。

在實(shí)際的block調(diào)用函數(shù)里白翻,比如__doBlockA_block_invoke_0,我們能看到在調(diào)用___doBlockA_block_invoke_0函數(shù)中先從r0 + 20地址初開始讀取了4個字節(jié)的數(shù)據(jù)到r0中,這額外的4個字節(jié)也就是block從外部捕獲到那個整數(shù)滤馍,然后調(diào)用foo函數(shù)岛琼。


如果捕獲的是一個對象類型呢?

我們需要考慮的下一個問題是如果block捕獲的外部變量是一個對象類型比如NSString而不是一個整數(shù)巢株,那么會發(fā)生什么槐瑞,看看下面的代碼:

捕獲對象類型的block

doBlockA函數(shù)我就不講了,和之前的一樣阁苞,沒有太多的變化困檩。有點(diǎn)意思的是這個block的descriptor結(jié)構(gòu)體指針:
descriptor

注意這邊有兩個函數(shù)指針:__copy_helper_block__destroy_helper_block。下面是這些函數(shù)的定義:
___copy_helper_block_和___destroy_helper_block_

我假設(shè)block被拷貝和銷毀正是因為有這兩個函數(shù)那槽,那么他們一定會對block捕獲的對象進(jìn)行retain和release操作悼沿。拷貝函數(shù)接收兩個參數(shù)(r0r1)骚灸,而銷毀函數(shù)接收一個參數(shù)糟趾。可以看出所有的拷貝和銷毀任務(wù)都應(yīng)該是由__Block_object_assign__Block_object_dispose兩個函數(shù)完成的甚牲。這兩個函數(shù)位于block的運(yùn)行時代碼中拉讯,是LLVM里面compiler-rt工程的一部分。

如果你想繼續(xù)了解runtime中有關(guān)block的代碼鳖藕,可以去http://compiler-rt.llvm.org下載相關(guān)源代碼魔慷。尤其要去看看runtime.c這個文件。


下一篇講啥?

下一篇準(zhǔn)備深入了解一下block在runtime中的Block_copy著恩,看看它是如何工作的院尔。這也能讓我們能更好的理解上面在block捕獲的外部對象時創(chuàng)建的__copy_helper_block__destroy_helper_block函數(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喉誊,一起剝皮案震驚了整個濱河市邀摆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伍茄,老刑警劉巖栋盹,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異敷矫,居然都是意外死亡例获,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門曹仗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來榨汤,“玉大人,你說我怎么就攤上這事怎茫∈蘸荆” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蜜宪。 經(jīng)常有香客問我虫埂,道長,這世上最難降的妖魔是什么圃验? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任掉伏,我火速辦了婚禮,結(jié)果婚禮上损谦,老公的妹妹穿的比我還像新娘岖免。我一直安慰自己岳颇,他們只是感情好照捡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著话侧,像睡著了一般栗精。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞻鹏,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天悲立,我揣著相機(jī)與錄音,去河邊找鬼新博。 笑死薪夕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赫悄。 我是一名探鬼主播原献,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼埂淮!你這毒婦竟也來了姑隅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤倔撞,失蹤者是張志新(化名)和其女友劉穎讲仰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痪蝇,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鄙陡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了躏啰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柔吼。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丙唧,靈堂內(nèi)的尸體忽然破棺而出愈魏,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布培漏,位于F島的核電站溪厘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏牌柄。R本人自食惡果不足惜畸悬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望珊佣。 院中可真熱鬧蹋宦,春花似錦、人聲如沸咒锻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惑艇。三九已至蒿辙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間滨巴,已是汗流浹背思灌。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恭取,地道東北人泰偿。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像蜈垮,于是被迫代替她去往敵國和親耗跛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

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

  • 前言 Blocks是C語言的擴(kuò)充功能窃款,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,757評論 0 23
  • 一课兄、Objective-C發(fā)展史 Objective-C從1983年誕生,已經(jīng)走過了30多年的歷程晨继。隨著時間的推移...
    沒事蹦蹦閱讀 5,814評論 12 34
  • 《Objective-C高級編程》這本書就講了三個東西:自動引用計數(shù)烟阐、block、GCD紊扬,偏向于從原理上對這些內(nèi)容...
    WeiHing閱讀 9,798評論 10 69
  • 1: 什么是block蜒茄?1.0: Block的語法1.1: block編譯轉(zhuǎn)換結(jié)構(gòu)1.2: block實(shí)際結(jié)構(gòu) 2...
    iYeso閱讀 831評論 0 5
  • 目錄 Block底層解析什么是block?block編譯轉(zhuǎn)換結(jié)構(gòu)block實(shí)際結(jié)構(gòu)block的類型NSConcre...
    tripleCC閱讀 33,147評論 32 388