本系列文章主要是對《Objective-C 高級編程》這本書作的讀書筆記總結,除了這本書中的內容之外,也加上了本身對開發技術的理解和一些我的的經驗分享。編程
經過 clang(LLVM編譯器)命令轉換:函數
clang -rewrite-objc 源代碼的文件名
先來看一個最簡單的blockthis
int main() { void (^blk)(void) = ^{ printf("我是block\n"); }; blk(); return 0; }
將以上源代碼轉換成C++源代碼指針
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; //block結構體 struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; //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; } }; // 未來被調用的block內部的代碼:block值被轉換爲C的函數代碼 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)}; //main 函數 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; }
看下面的代碼code
^ { printf("我是block\n"); };
能夠看到,變換後的源代碼中也含有相同的表達式對象
static void __main_block_func_0(struct __main_block_impl_0 * __cself) { printf("我是block\n"); }
經過block使用的匿名函數實際上被做爲簡單的C語言函數來處理。根據block語法所屬的函數名(此處爲main)和該block語法在該函數出現的順序值(此處爲0)來給函數命名 __main_block_func_0開發
該函數的參數 __cself 至關於C++實例方法中指向實例自身的變量this,或是Object-C實例方法中指向對象自身的變量self,即參數 __cself 爲指向block值的變量。編譯器
這裏的 __main_block_func_0 函數並無使用到 __cself。使用 __cself 的例子將在後面介紹,咱們先來看看該參數的聲明,參數 __cself 是 __main_block_func_0 結構體的指針。it
struct __main_block_impl_0 * __cself
該結構體的聲明以下:編譯
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0 * Desc; }
第一個成員變量是 impl,咱們先來看一下 __block_impl 結構體的聲明。
struct __block_impl { void *isa; int Flags; // 標誌 int Reserved; void *FuncPtr; }
接下來咱們看一下第二個成員變量 Desc指針
struct __main_block_desc_0 { unsigned long reserved; // 版本升級所需的區域 unsigned long Block_size; // block的大小 }
那麼,咱們再來看一下初始化這些結構體的 _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; }
NSConcreteStackBlock用於初始化 __block_impl結構體 的isa成員,後面將會講解。咱們先來看一下該構造函數的調用。
void (*blk)(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(__main_block_func_0, &__main_block_desc_0_DATA); struct __main_block_impl_0 *blk = &temp;
這樣就容易理解了。該源代碼將 __main_block_impl_0結構體 類型的自動變量,賦值給 __main_block_impl_0結構體指針類型的變量blk
下面就來看看 __main_block_impl_0結構體實例構造參數。
__main_block_impl_0 (__main_block_func_0, &__main_block_desc_0_DATA);
第一個參數是由block語法轉換爲C語言函數指針。第二個參數是做爲靜態全局變量初始化的 __main_block_desc_0 結構體的實例指針。
如下爲 __main_block_desc_0 結構體實例的初始化部分代碼,能夠看到源代碼使用 __main_block_impl_0 結構體的實例大小,進行初始化。
static struct __main_block_desc_0 __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0) };
下面看看棧上的 __main_block_impl_0結構體 實例是如何根本這些參數進行初始化的。
struct __main_block_impl_0 { void *isa; int Flags; int Reserved; void *FuncPtr; struct __main_block_desc_0* Desc; };
該結構體根據構造函數會像下面這樣進行初始化
isa = &_NSConcreteStackBlock; Flags = 0; Reserved = 0; FuncPtr = __main_block_func_0; Desc = &__main_block_desc_0_DATA;
將 __main_block_func_0 函數指針賦給成員變量 FuncPtr
blk();
轉換後的源代碼以下
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
去掉轉換部分:
(*blk -> impl.FuncPtr)(blk);
這就是簡單地使用函數指針調用函數。