Block的實質以及實現
Block的實現是通過結構體的方式實現吻谋,在編譯的過程中妻导,將Block生成對應的結構體嗤瞎,在結構體中記錄Block的匿名函數女器,以及使用到的自動變量酸役,在最后的使用中,通過Block結構體實例訪問成員中存放的匿名函數地址調用匿名函數驾胆,并將自身作為參數傳遞涣澡。
所謂的匿名函數也不是完全匿名的,編譯時還是會按照匿名函數所在的方法丧诺、Block入桂、順序命名,并記錄到Block結構體中驳阎。
將含有Block語法的源代碼變換為C++源代碼抗愁。
clang -rewrite-objc 源代碼文件名
執(zhí)行該命令后,會在源代碼文件路徑下生成一個與源代碼文件名稱相同后綴為.cpp的文件呵晚。
源代碼
int main() {
void (^block) (void) = ^ {
printf("block\n");
};
block();
}
轉換后的代碼蜘腌,將其中有效的信息提取出來。 如果看的比較暈可以結合文章末尾圖一起食用饵隙,效果更好撮珠。
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__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;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("block\n");
}
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 (*block) (void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
變換后的源代碼中也含有相同的表達式,通過Block使用的匿名函數實際上被作為最為簡單的C語言函數來處理金矛,另外芯急,根據Block語法所屬的函數名(此處為main)和該Block語法所在該函數出現的順序值(此處為0)來給經clang變換的函數命名。
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("block\n");
}
參數__cself 為 __main_block_impl_0 結構體的指針驶俊。
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//構造函數
__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;
}
};
第一個成員變量impl:__block_impl結構體娶耍。所有block結構體中都存在的成員。記錄著基本信息饼酿,isa類似對象中的isa指針榕酒,FuncPtr記錄著block對應的實現函數胚膊。
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
第二個成員變量Desc:__main_block_desc_0結構體。
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)};
初始化結構體的__main_block_impl_0結構體構造函數
__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;
}
__main_block_impl_0結構體構造函數構造函數的調用:
void (*block) (void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
轉換下:
struct _main_block_impl_0 temp =
__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);
struct _main_block_impl_0 *block = &temp;
該源代碼將__main_block_impl_0結構體類型的自動變量想鹰,即棧上生成的__main_block_impl_0結構體實例的指針澜掩,賦值給__main_block_impl_0結構體指針類型的變量block。
其中__main_block_impl_0構造函數的參數一為:Block語法轉換的C語言函數指針杖挣。參數二是作為靜態(tài)全局變量初始化的_main_block_desc_0結構體實例指針肩榕。
以下為_main_block_desc_0結構體實例初始化部分代碼:
__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
使用__main_block_impl_0結構體實例的大小進行出初始化。
使用該block:
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
去掉轉換部分等同:
(*block->impl.FuncPtr)(blcok);
簡單的函數指針調用函數,由Block語法轉換的__main_block_func_0函數指針被賦值成員變量FuncPtr中惩妇,另外也說明了株汉。_main_block_func_0函數的參數_cself指向Block值。在調用該函數的源代碼中可以看出Block正是作為參數進行傳遞歌殃。
- Block的實質即為Objective-C的對象乔妈。
最后奉上一張大圖:
待續(xù)。
參考
《Objective-C高級編程》