學(xué)習(xí)小碼哥OC底層視頻筆記 - Block模塊
慢慢學(xué),總會成長的
1、生成代碼
- 先定義一段代碼:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 定義一個簡單的block
void (^block)(void) = ^{
NSLog(@"block");
};
// 執(zhí)行block
block();
}
return 0;
}
- 將這段代碼轉(zhuǎn)成 C++ 代碼盲厌,打開終端署照,
cd
到對應(yīng)的main.c
文件目錄中,輸入:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
- 將
main.cpp
添加到項目中吗浩,為了編譯通過建芙,在編譯列表中取消main.cpp
文件,具體操作:
取消main.cpp編譯.png
2懂扼、main.cpp 文件分析
現(xiàn)在提取
main.cpp
中主要內(nèi)容代碼岁钓,進行詳細分析
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
-
block
的本質(zhì)結(jié)構(gòu)是一個結(jié)構(gòu)體
struct __main_block_impl_0 {
struct __block_impl impl; // 包含邏輯代碼
struct __main_block_desc_0* Desc; // 包含block大小
// 構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
// 此處block的類型是 _NSConcreteStackBlock,又一次證明block是對象
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
-
__main_block_impl_0
結(jié)構(gòu)體中impl
的類型是__block_impl
結(jié)構(gòu)體微王,由于結(jié)構(gòu)體地址等于其內(nèi)部第一個成員的地址屡限,所以__main_block_impl_0
結(jié)構(gòu)體的地址為impl
的地址,最后我們可以把__main_block_impl_0
結(jié)構(gòu)體等價于:
struct __main_block_impl_0 {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __main_block_desc_0* Desc;
// 構(gòu)造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
isa = &_NSConcreteStackBlock;
Flags = flags;
FuncPtr = fp;
Desc = desc;
}
};
- 構(gòu)造
__main_block_impl_0
結(jié)構(gòu)體炕倘,傳入的第一個參數(shù)
/// 執(zhí)行block內(nèi)部的邏輯代碼
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_96_0vbtlxxx6nd01n2n42jqy4xc0000gn_T_main_569e20_mi_0);
}
- 構(gòu)造
__main_block_impl_0
結(jié)構(gòu)體钧大,傳入的第二個參數(shù)
/// reserved = 0
/// Block_size = sizeof(struct __main_block_impl_0),即該block結(jié)構(gòu)體的大小
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.m
文件中執(zhí)行代碼在main.cpp
生成的代碼如下:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
// 定義一個簡單的block
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
// 執(zhí)行block
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
- 由于小括號一般為強制轉(zhuǎn)換罩旋,此處為了好理解啊央,把小括號取消,做代碼簡化涨醋,簡化后:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
/**
此處定義block變量
說明:傳入兩個參數(shù)瓜饥,第一個是執(zhí)行邏輯代碼的函數(shù),第二個是block描述信息的地址
再簡化:
void (*block)(void) = &結(jié)構(gòu)體
說明:指向的是結(jié)構(gòu)體的地址
執(zhí)行說明:
1浴骂、創(chuàng)建一個結(jié)構(gòu)體乓土,將代碼塊、block大小存入結(jié)構(gòu)體內(nèi)部溯警,再將該結(jié)構(gòu)體地址指給block變量
2趣苏、結(jié)構(gòu)體內(nèi)部,由FuncPtr指針存儲函數(shù)梯轻,Des存儲描述內(nèi)容
*/
void (*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
/**
執(zhí)行block內(nèi)部的代碼
執(zhí)行過程:
1食磕、由上面完整代碼可知,block被強制轉(zhuǎn)化為(__block_impl *)喳挑,原因是上面解釋過的彬伦,地址相同。
2伊诵、__block_impl結(jié)構(gòu)體內(nèi)部有 void *FuncPtr 成員单绑,指向block內(nèi)部的執(zhí)行代碼,即 FuncPtr = __main_block_func_0日戈。
3询张、__main_block_func_0執(zhí)行需要傳入block自己。根據(jù)__main_block_func_0函數(shù)可知浙炼。
4份氧、直接調(diào)用FuncPtr指向的__main_block_func_0函數(shù)唯袄,執(zhí)行內(nèi)部邏輯代碼。
*/
block->FuncPtr(block);
}
return 0;
}
3蜗帜、總結(jié)
定義一個block恋拷,是生成了一個結(jié)構(gòu)體。block將邏輯代碼函數(shù)厅缺、block描述內(nèi)容作為參數(shù)蔬顾,傳入block結(jié)構(gòu)體的構(gòu)造函數(shù),
FuncPtr
存儲代碼塊湘捎,Desc
存儲大小诀豁。執(zhí)行block,強轉(zhuǎn)類型后窥妇,直接調(diào)用
FuncPtr
變量舷胜,執(zhí)行代碼塊。