成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

移動開發 iOS
上一篇我們總結了各個情況下,block及其引用到的內存位置情況。接下來幾篇,我們將剖析編譯器轉碼以及運行時庫源碼來一探block的究竟。

block到底是什么

我們使用clang的rewrite-objc命令來獲取轉碼后的代碼。

1、block的底層實現

我們來看看最簡單的一個block:

 

這個block僅僅打印棧變量i和j的值,其被clang轉碼為:

首先是一個結構體__main_block_impl_0(從圖二中的***一行可以看到,block是一個指向__main_block_impl_0的指針,初始化后被類型強轉為函數指針),其中包含的__block_impl是一個公共實現(學過c語言的同學都知道,__main_block_impl_0的這種寫法表示其可以被類型強轉為__block_impl類型):

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

isa指針說明block可以成為一個objc對象。

__main_block_impl_0的意思是main函數中的第0個block的implementation,這就是這個block的主體了。

這個結構體的構造函數的參數:

block實際執行代碼所在的函數的指針,當block真正被執行時,實際上是調用了這個函數,其命名也是類似的方式。

block的描述結構體,注意這個結構體聲明結束時就創建了一個唯一的desc,這個desc包含了block的大小,以及復制和析構block時需要額外調用的函數。

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

***是一個標記值,內部實現需要用到的。(我用計算器看了一下,570425344這個值等于1<<29,即BLOCK_HAS_DESCRIPTOR這個枚舉值)

所以,我們可以看到:

為什么上一篇我們說j已經不是原來的j了,因為j是作為參數傳入了block的構造函數,進行了值復制。

帶有__block標記的變量會被取地址來傳入構造函數,為修改其值奠定了基礎

接下來是block執行函數__main_block_func_0:

其唯一的參數是__main_block_impl_0的指針,我們看到printf語句的數據來源都取自__cself這個指針,比較有意思的是i的取值方式(帶有__block標記的變量i被轉碼為一個結構體),先取__forward指針,再取i,這為將i復制到堆中奠定了基礎。

再下來是預定義好的兩個復制/釋放輔助函數,其作用后面會講到。 

***是block的描述信息結構體 __main_block_desc_0,其包含block的內存占用長度,已經復制/釋放輔助函數的指針,其聲明結束時,就創建了一個名為__main_block_desc_0_DATA的結構體,我們看它構造時傳入的值,這個DATA結構體的作用就一目了然了:

長度用sizeof計算,輔助函數的指針分別為上面預定義的兩個輔助函數。

注意,如果這個block沒有使用到需要在block復制時進行copy/retian的變量,那么desc中不會有輔助函數

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

2、構造一個block

我們進入main函數:

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

而圖一中的***一行(創建一個block),在圖二中,成為了對__main_block_impl_0的構造函數的調用,傳入的參數的意義上面我們已經講過了。

所以構造一個block就是創建了__main_block_impl_0 這個c++類的實例。

3、調用一個block

調用一個block的寫法很簡單,與調用c語言函數的語法一樣:

  1. blk(); 

其轉碼后的語句:

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

將blk這個函數指針類型強轉為__block_impl類型,然后取其執行函數指針,然后將此指針類型強轉為返回void*并接收一個__block_impl*的函數指針,***調用這個函數,傳入強轉為__block_impl*類型的blk,

即調用了前述的函數__main_block_func_0

4、objective-c類成員函數中的block

源碼如下:

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

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

轉碼如下:

  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并無太多差別,只是一些標記值可能不同,為了標記其是objc方法中的blcok。

注意其構造函數的參數:OBJ1 *_oj

這個_oj在block復制到heap時,會被retain,而_oj與self根本就是相等的,所以,最終retain的就是self,所以如果當前實例持有了這個block,retain循環就形成了。

而一旦為其增加了__block標記:

  1. - (void)of1 
  2.     __block OBJ1 *bSelf = self; 
  3.     ^{ printf("%d", bSelf.oi); }; 
  4. }其轉碼則變為: 
  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. //聲明處變為 
  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結構體創建了自己的copy/dispose輔助函數,33554432(即1<<25 BLOCK_HAS_COPY_DISPOSE)這個值告訴系統,我們的bSelf結構體具有copy/dispose輔助函數。

而131這個參數(二進制1000 0011,即BLOCK_FIELD_IS_OBJECT (3) |BLOCK_BYREF_CALLER(128))

中的BLOCK_BYREF_CALLER在內部實現中告訴系統不要進行retain或者copy,

也就是說,在 __block bSelf 被復制至heap上時,系統會發現有輔助函數,而輔助函數調用后,并不retain或者copy 其結構體內的bSelf。

這樣就避免了循環retain。

小結:

當我們創建一個block,并調用之,編譯器為我們做的事情如下:

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

2.將我們的創建代碼轉碼為block_impl的構造語句。

3.將我們的執行語句轉碼為對block的執行函數的調用。

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

責任編輯:閆佳明 來源: dreamingwish
相關推薦

2013-07-19 14:35:59

iOS中BlockiOS開發學習

2015-08-20 13:43:17

NFV網絡功能虛擬化

2016-11-16 09:06:59

2010-05-26 19:12:41

SVN沖突

2015-09-07 13:52:04

2021-07-28 21:49:01

JVM對象內存

2010-05-17 09:13:35

2021-06-07 08:18:12

云計算云端阿里云

2014-03-12 11:11:39

Storage vMo虛擬機

2009-06-01 09:04:44

Google WaveWeb

2010-05-11 10:19:17

VMforceJava云計算

2018-03-01 09:33:05

軟件定義存儲

2009-09-15 15:34:33

Google Fast

2016-04-06 09:27:10

runtime解密學習

2023-11-02 09:55:40

2012-08-17 09:27:34

奧運會云計算虛擬化

2024-02-14 09:00:00

機器學習索引ChatGPT

2015-09-06 13:40:02

HTTP網絡協議

2024-11-11 16:36:41

2025-01-07 15:07:13

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩一| 亚洲免费在线观看 | 一区二区三区精品视频 | 黄色永久免费 | 日本精品一区二区三区视频 | 欧美视频网 | 国产精品久久国产精品久久 | 精品欧美一区二区三区久久久 | 国产精品久久久久久久久久免费看 | 在线男人天堂 | 日日夜精品视频 | 欧美日韩国产中文字幕 | 国产一区二区三区 | 国产精品亚洲成在人线 | 国产一区二区黑人欧美xxxx | 91精品国产乱码久久久久久久久 | 在线视频91| 国产免费观看久久黄av片涩av | 国产福利在线视频 | a级免费黄色片 | 澳门永久av免费网站 | 国产传媒在线观看 | 亚洲欧美国产精品一区二区 | 成人高清视频在线观看 | 亚洲欧美激情精品一区二区 | 亚洲欧美日韩国产综合 | 日本黄色大片免费 | 在线中文字幕亚洲 | 2021狠狠天天天 | 亚洲精品成人在线 | 国产精品视频一区二区三区, | 成年免费视频 | 久久久妇女国产精品影视 | 久久精品成人 | 国产欧美日韩在线观看 | 99伊人网| 免费午夜视频在线观看 | 草久久 | 懂色av一区二区三区在线播放 | 视频在线观看一区二区 | 亚洲一区二区三区观看 |