自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS中block介紹(三)揭開神秘面紗(上)

移動(dòng)開發(fā) iOS
上一篇我們總結(jié)了各個(gè)情況下,block及其引用到的內(nèi)存位置情況。接下來幾篇,我們將剖析編譯器轉(zhuǎn)碼以及運(yùn)行時(shí)庫源碼來一探block的究竟。

block到底是什么

我們使用clang的rewrite-objc命令來獲取轉(zhuǎn)碼后的代碼。

1、block的底層實(shí)現(xiàn)

我們來看看最簡(jiǎn)單的一個(gè)block:

 

這個(gè)block僅僅打印棧變量i和j的值,其被clang轉(zhuǎn)碼為:

首先是一個(gè)結(jié)構(gòu)體__main_block_impl_0(從圖二中的***一行可以看到,block是一個(gè)指向__main_block_impl_0的指針,初始化后被類型強(qiáng)轉(zhuǎn)為函數(shù)指針),其中包含的__block_impl是一個(gè)公共實(shí)現(xiàn)(學(xué)過c語言的同學(xué)都知道,__main_block_impl_0的這種寫法表示其可以被類型強(qiáng)轉(zhuǎn)為__block_impl類型):

  1. struct __block_impl { 
  2.   void *isa; 
  3.   int Flags; 
  4.   int Reserved; 
  5.   void *FuncPtr; 
  6. };

isa指針說明block可以成為一個(gè)objc對(duì)象。

__main_block_impl_0的意思是main函數(shù)中的第0個(gè)block的implementation,這就是這個(gè)block的主體了。

這個(gè)結(jié)構(gòu)體的構(gòu)造函數(shù)的參數(shù):

block實(shí)際執(zhí)行代碼所在的函數(shù)的指針,當(dāng)block真正被執(zhí)行時(shí),實(shí)際上是調(diào)用了這個(gè)函數(shù),其命名也是類似的方式。

block的描述結(jié)構(gòu)體,注意這個(gè)結(jié)構(gòu)體聲明結(jié)束時(shí)就創(chuàng)建了一個(gè)唯一的desc,這個(gè)desc包含了block的大小,以及復(fù)制和析構(gòu)block時(shí)需要額外調(diào)用的函數(shù)。

接下來是block所引用到的變量們

***是一個(gè)標(biāo)記值,內(nèi)部實(shí)現(xiàn)需要用到的。(我用計(jì)算器看了一下,570425344這個(gè)值等于1<<29,即BLOCK_HAS_DESCRIPTOR這個(gè)枚舉值)

所以,我們可以看到:

為什么上一篇我們說j已經(jīng)不是原來的j了,因?yàn)閖是作為參數(shù)傳入了block的構(gòu)造函數(shù),進(jìn)行了值復(fù)制。

帶有__block標(biāo)記的變量會(huì)被取地址來傳入構(gòu)造函數(shù),為修改其值奠定了基礎(chǔ)

接下來是block執(zhí)行函數(shù)__main_block_func_0:

其唯一的參數(shù)是__main_block_impl_0的指針,我們看到printf語句的數(shù)據(jù)來源都取自__cself這個(gè)指針,比較有意思的是i的取值方式(帶有__block標(biāo)記的變量i被轉(zhuǎn)碼為一個(gè)結(jié)構(gòu)體),先取__forward指針,再取i,這為將i復(fù)制到堆中奠定了基礎(chǔ)。

再下來是預(yù)定義好的兩個(gè)復(fù)制/釋放輔助函數(shù),其作用后面會(huì)講到。 

***是block的描述信息結(jié)構(gòu)體 __main_block_desc_0,其包含block的內(nèi)存占用長度,已經(jīng)復(fù)制/釋放輔助函數(shù)的指針,其聲明結(jié)束時(shí),就創(chuàng)建了一個(gè)名為__main_block_desc_0_DATA的結(jié)構(gòu)體,我們看它構(gòu)造時(shí)傳入的值,這個(gè)DATA結(jié)構(gòu)體的作用就一目了然了:

長度用sizeof計(jì)算,輔助函數(shù)的指針分別為上面預(yù)定義的兩個(gè)輔助函數(shù)。

注意,如果這個(gè)block沒有使用到需要在block復(fù)制時(shí)進(jìn)行copy/retian的變量,那么desc中不會(huì)有輔助函數(shù)

至此,一個(gè)block所有的部件我們都看齊全了,一個(gè)主體,一個(gè)真正的執(zhí)行代碼函數(shù),一個(gè)描述信息(可能包含兩個(gè)輔助函數(shù))。

2、構(gòu)造一個(gè)block

我們進(jìn)入main函數(shù):

圖一中的第三行(block的聲明),在圖二中,轉(zhuǎn)化為一個(gè)函數(shù)指針的聲明,并且都沒有被賦予初始值。

而圖一中的***一行(創(chuàng)建一個(gè)block),在圖二中,成為了對(duì)__main_block_impl_0的構(gòu)造函數(shù)的調(diào)用,傳入的參數(shù)的意義上面我們已經(jīng)講過了。

所以構(gòu)造一個(gè)block就是創(chuàng)建了__main_block_impl_0 這個(gè)c++類的實(shí)例。

3、調(diào)用一個(gè)block

調(diào)用一個(gè)block的寫法很簡(jiǎn)單,與調(diào)用c語言函數(shù)的語法一樣:

  1. blk(); 

其轉(zhuǎn)碼后的語句:

  1. ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); 

將blk這個(gè)函數(shù)指針類型強(qiáng)轉(zhuǎn)為__block_impl類型,然后取其執(zhí)行函數(shù)指針,然后將此指針類型強(qiáng)轉(zhuǎn)為返回void*并接收一個(gè)__block_impl*的函數(shù)指針,***調(diào)用這個(gè)函數(shù),傳入強(qiáng)轉(zhuǎn)為__block_impl*類型的blk,

即調(diào)用了前述的函數(shù)__main_block_func_0

4、objective-c類成員函數(shù)中的block

源碼如下:

  1. - (void)of1 
  2.     OBJ1* oj = self; 
  3.     void (^oblk)(void) = ^{ printf("%d\n", oj.oi);}; 
  4.     Block_copy(oblk); 

這里我故意將self賦值給oj這個(gè)變量,是為了驗(yàn)證前一章提出的一個(gè)結(jié)論:無法通過簡(jiǎn)單的間接引用self來防止retain循環(huán),要避免循環(huán),我們需要__block標(biāo)記(多謝樓下網(wǎng)友的提醒)

轉(zhuǎn)碼如下:

  1. struct __OBJ1__of1_block_impl_0 { 
  2.   struct __block_impl impl; 
  3.   struct __OBJ1__of1_block_desc_0* Desc; 
  4.   OBJ1 *oj; 
  5.   __OBJ1__of1_block_impl_0(void *fp, struct __OBJ1__of1_block_desc_0 *desc, OBJ1 *_oj, int flags=0) : oj(_oj) { 
  6.     impl.isa = &_NSConcreteStackBlock; 
  7.     impl.Flags = flags; 
  8.     impl.FuncPtr = fp; 
  9.     Desc = desc; 
  10.   } 
  11. }; 
  12. static void __OBJ1__of1_block_func_0(struct __OBJ1__of1_block_impl_0 *__cself) { 
  13.   OBJ1 *oj = __cself->oj; // bound by copy 
  14.  printf("%d\n", ((int (*)(id, SEL))(void *)objc_msgSend)((id)oj, sel_registerName("oi")));} 

objc方法中的block與c中的block并無太多差別,只是一些標(biāo)記值可能不同,為了標(biāo)記其是objc方法中的blcok。

注意其構(gòu)造函數(shù)的參數(shù):OBJ1 *_oj

這個(gè)_oj在block復(fù)制到heap時(shí),會(huì)被retain,而_oj與self根本就是相等的,所以,最終retain的就是self,所以如果當(dāng)前實(shí)例持有了這個(gè)block,retain循環(huán)就形成了。

而一旦為其增加了__block標(biāo)記:

  1. - (void)of1 
  2.     __block OBJ1 *bSelf = self; 
  3.     ^{ printf("%d", bSelf.oi); }; 
  4. }其轉(zhuǎn)碼則變?yōu)椋?nbsp;
  5.  
  6. //增加了如下行 
  7. struct __Block_byref_bSelf_0 { 
  8.   void *__isa; 
  9. __Block_byref_bSelf_0 *__forwarding; 
  10.  int __flags; 
  11.  int __size; 
  12.  void (*__Block_byref_id_object_copy)(void*, void*); 
  13.  void (*__Block_byref_id_object_dispose)(void*); 
  14.  OBJ1 *bSelf; 
  15. }; 
  16. static void __Block_byref_id_object_copy_131(void *dst, void *src) { 
  17.  _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131); 
  18. static void __Block_byref_id_object_dispose_131(void *src) { 
  19.  _Block_object_dispose(*(void * *) ((char*)src + 40), 131); 
  20.  
  21. //聲明處變?yōu)?/span> 
  22.     __block __Block_byref_bSelf_0 bSelf = {(void*)0,(__Block_byref_bSelf_0 *)&bSelf, 33554432, sizeof(__Block_byref_bSelf_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, self}; 

clang為我們的bSelf結(jié)構(gòu)體創(chuàng)建了自己的copy/dispose輔助函數(shù),33554432(即1<<25 BLOCK_HAS_COPY_DISPOSE)這個(gè)值告訴系統(tǒng),我們的bSelf結(jié)構(gòu)體具有copy/dispose輔助函數(shù)。

而131這個(gè)參數(shù)(二進(jìn)制1000 0011,即BLOCK_FIELD_IS_OBJECT (3) |BLOCK_BYREF_CALLER(128))

中的BLOCK_BYREF_CALLER在內(nèi)部實(shí)現(xiàn)中告訴系統(tǒng)不要進(jìn)行retain或者copy,

也就是說,在 __block bSelf 被復(fù)制至heap上時(shí),系統(tǒng)會(huì)發(fā)現(xiàn)有輔助函數(shù),而輔助函數(shù)調(diào)用后,并不retain或者copy 其結(jié)構(gòu)體內(nèi)的bSelf。

這樣就避免了循環(huán)retain。

小結(jié):

當(dāng)我們創(chuàng)建一個(gè)block,并調(diào)用之,編譯器為我們做的事情如下:

1.創(chuàng)建block所有的部件代碼:一個(gè)主體,一個(gè)真正的執(zhí)行代碼函數(shù),一個(gè)描述信息(可能包含兩個(gè)輔助函數(shù))。

2.將我們的創(chuàng)建代碼轉(zhuǎn)碼為block_impl的構(gòu)造語句。

3.將我們的執(zhí)行語句轉(zhuǎn)碼為對(duì)block的執(zhí)行函數(shù)的調(diào)用。

下一篇我們將剖析runtime.c的源碼,并理解block的堆棧轉(zhuǎn)換。

責(zé)任編輯:閆佳明 來源: dreamingwish
相關(guān)推薦

2013-07-19 14:35:59

iOS中BlockiOS開發(fā)學(xué)習(xí)

2015-08-20 13:43:17

NFV網(wǎng)絡(luò)功能虛擬化

2016-11-16 09:06:59

2021-07-28 21:49:01

JVM對(duì)象內(nèi)存

2010-05-26 19:12:41

SVN沖突

2015-09-07 13:52:04

2010-05-17 09:13:35

2014-03-12 11:11:39

Storage vMo虛擬機(jī)

2021-06-07 08:18:12

云計(jì)算云端阿里云

2009-09-15 15:34:33

Google Fast

2023-11-02 09:55:40

2016-04-06 09:27:10

runtime解密學(xué)習(xí)

2010-05-11 10:19:17

VMforceJava云計(jì)算

2009-06-01 09:04:44

Google WaveWeb

2018-03-01 09:33:05

軟件定義存儲(chǔ)

2015-09-06 10:54:29

HTTP網(wǎng)絡(luò)協(xié)議

2015-09-08 10:06:15

2010-09-17 14:57:34

JAVA數(shù)據(jù)類型

2010-06-17 10:53:25

桌面虛擬化

2011-08-02 08:59:53

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)