Block的實質:函數
#import <Foundation/Foundation.h> int main() { void(^blk)(void)=^{printf("Block");}; blk(); return 0; }
使用clang將該block轉換。spa
clang -rewrite-objc 源代碼文件
轉換後造成的block文件是:指針
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
struct __main_block_impl_0 {code
struct __block_impl impl;對象
struct __main_block_desc_0* Desc;blog
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {it
impl.isa = &_NSConcreteStackBlock;io
impl.Flags = flags;class
impl.FuncPtr = fp;import
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block");}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main() {
void(*blk)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
下面咱們就來具體分析一下這些源代碼:
首先咱們看到
^{printf("Block");};
轉換後稱爲
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf("Block");}
如上所示的,block使用的匿名函數,其實被轉換成c語言函數處理,,該函數中 的_cself是_main_block_func_0結構體的指針。
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc;
具備兩個成員變量,_block_impld 代碼以下:
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
第二個成員變量是
__main_block_desc_0:其代碼以下:
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
}
這個包含了從此版本升級所需的區域以及block的大小。
下面咱們來看一下初始化含有這些數據的結構體的代碼:
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }
在這裏面NSConcreteStackBlock用來初始化,接下來咱們對構造函數的調用:
void(*blk)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
由於轉換太多實在太難以理解,因此咱們去掉轉換的部分,這樣獲得的結果是:
struct _main_block_impl_0 tmp=_main_block_impl_0(_main_block_func_0,&_main_block_desc_DATA); struct _main_block_impl_0 *blk=&tmp;
這樣一來咱們能夠清楚的看到,
void(^blk)(void)=^{printf("Block");};
這個語句的實現了_main_block_impl_0的兩個變量,一個功能函數,一個desc,將block語法生成的block類型對象再賦值給blk,至關於將實例的指針賦值給blk,_main_block_desc_DATA主要是對_main_block_impl_0的結構體實例的大小進行初始化.
blk();
這部分代碼轉換成爲了
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
咱們同樣去掉轉換的部分獲得:
(*blk)->impl.Funcptr(blk)
這實際上是最簡單的函數指針調用函數,正如咱們剛纔確認的那樣,在調用函數中咱們看出block是做爲參數進行傳遞的。
那麼到底什麼是NSConcreteStackBlock呢?
爲了理解他首先咱們須要理解oc類和對象的實質,其實,所謂的block就是對象,「id」這一變量用於存儲oc對象,NSConcreteStackBlock就至關於c結構體實例,將block做爲oc對象處理時,該類的信息就放置於NSConcreteStackBlock處。