int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; void (^block)(int, int) = ^(int a , int b){ NSLog(@"this is a block! -- %d", age); NSLog(@"this is a block!"); NSLog(@"this is a block!"); NSLog(@"this is a block!"); }; block(10, 10); } return 0; }
使用命令行將代碼轉化爲c++與OC代碼進行比較
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
命令代碼ios
// 編譯後代碼 int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; int age = 10; void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); ((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10); } return 0; }
定義block變量
代碼c++
// 定義block變量代碼 void(*block)(int ,int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); // 能夠簡化爲下列 // void (*block)(int, int) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, age);
上述定義代碼中,能夠發現,block定義中調用了__main_block_impl_0函數,而且將__main_block_impl_0函數的地址賦值給了block。那麼咱們來看一下__main_block_impl_0函數內部結構。sass
__main_block_imp_0結構體iphone
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
(void *)__main_block_func_0
參數函數
```C++ static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) { int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_1); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_2); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_3); } ```
&__main_block_desc_0_DATA
參數this
C++ 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)};
命令行
age
參數
此時回過頭來查看__main_block_impl_0結構體3d
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp;// block 內部代碼塊地址 Desc = desc;// 存儲block 對象佔用的內存大小 } };
首先咱們看一下__block_impl第一個變量就是__block_impl結構體。指針
// __block_impl結構體內部 struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
咱們能夠發現__block_impl結構體內部就有一個isa指針。所以能夠證實block本質上就是一個oc對象。而在構造函數中將函數中傳入的值分別存儲在__main_block_impl_0結構體實例中,最終將結構體的地址賦值給block。code
調用block執行內部代碼
((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10);
上面咱們知道,FunPtr中存儲着經過代碼塊封裝的函數地址,那麼調用此函數,也就是會執行代碼塊中的代碼。而且回頭查看__main_block_func_0函數,能夠發現第一個參數就是__main_block_impl_0類型的指針。也就是說將block傳入__main_block_func_0函數中,便於重中取出block捕獲的值。
經過代碼證實一下上述內容:
#import <Foundation/Foundation.h> struct __main_block_desc_0 { size_t reserved; size_t Block_size; }; 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; int age; }; int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; void (^block)(int, int) = ^(int a , int b){ NSLog(@"this is a block! -- %d", age); NSLog(@"this is a block!"); NSLog(@"this is a block!"); NSLog(@"this is a block!"); }; // 將底層的結構體強制轉化爲咱們本身寫的結構體,經過咱們自定義的結構體探尋block底層結構體 struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block; block(10, 10); } return 0; }
經過打斷點能夠看出咱們自定義的結構體能夠被賦值成功,以及裏面的值。
接下來斷點來到block代碼塊中,看一下堆棧信息中的函數調用地址。Debuf workflow -> always show Disassembly
經過上圖能夠看到地址確實和FuncPtr中的代碼塊地址同樣。