探尋Block的本質(3)—— 基礎類型的變量捕獲markdown
探尋Block的本質(5)—— 對象類型的變量捕獲iphone
探尋Block的本質(6)—— __block的深刻分析函數
上一篇,咱們初步認識了Block的一些基本知識。如今,咱們來一塊兒挖掘一下Block的底層結構。 首先仍是新建一個命令行項目
定義一個最最簡單的blockpost
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//Block的定義
void (^block)(void) = ^(){
NSLog(@"I am a block!");
};
//Block的調用
block();
}
return 0;
}
複製代碼
緊接着,經過xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
命令拿到編譯後的c++文件,添加到項目中。直接查看該文件的尾部 在main函數裏面,有不少強制類型轉換代碼,爲了便於理解,去掉這些轉換代碼(不影響原有的邏輯),將其main函數簡化成以下ui
//【1.2】
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//C++語法下,下面的函數是一個類的構造方法,函數名與類名相同,與oc裏面的init方法相似,返回結構對象
__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;
}
};
//【1.1】這個函數是block內部封裝的代碼塊執行邏輯
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_7__p19yp82j0xd2m_1k8fpr77z40000gn_T_main_65b8ed_mi_0);
}
//【1.3】
static struct __main_block_desc_0 {
size_t reserved;//保留變量,暫無用處
size_t Block_size;//block的大小
}__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
//main函數
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//【1】-------------- 【1.4】
void (*block)(void) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA));
//【2】
block->FuncPtr(block);
}
return 0;
}
複製代碼
下面來分析一下系統底層的執行的邏輯。 首先,根據oc代碼中main
函數裏面的第一句代碼spa
//Block的定義
void (^block)(void) = ^(){
NSLog(@"I am a block!");
};
複製代碼
系統會進行步驟【1.1】
,也就是會吧Block中的代碼塊封裝到函數static void __main_block_func_0(struct __main_block_impl_0 *__cself){}
裏面.命令行
接下來的步驟【1.2】
,系統會定義結構體struct __main_block_impl_0
做爲Block的底層數據結構,因爲是C++語法下的結構體,所以這個結構體裏面能夠定義函數,代碼中的函數爲這個結構體的構造函數__main_block_impl_0()
3d
接下來的【1.3】
步驟,系統又定義告終構體static struct __main_block_desc_0
,而且生成了一個實例__main_block_desc_0_DATA
,用來存放Block的相關描述信息。
最後是【1.4】
步驟,系統利用構造函數__main_block_impl_0()
生成的對象的地址賦值給block
指針。傳入的參數有兩個,分別爲 (1)__main_block_func_0
__main_block_impl_0
->impl
->funcPtr
(2)&__main_block_desc_0_DATA
static struct __main_block_desc_0
構造這樣步驟【1】
就結束了。再上一幅圖來呈現一下整個過程
接下來根據根據咱們的OC代碼
//【2】
block->FuncPtr(block);
複製代碼
系統會經過步驟【2】
完成了block的調用,也就是調用block內部封裝的那個函數指針。 block->FuncPtr(block);
⚠️小細節
block->FuncPtr(block);
爲何沒報錯?由於按照咱們的代碼結構,應該是block->impl->FuncPtr(block);
纔對。 且看struct __main_block_impl_0
中的第一個成員爲struct __block_impl impl;
,說明impl
是一個結構體,不是指針,因此能夠直接把它的內容搬到struct __main_block_impl_0
來理解所以
block->FuncPtr(block);
等價於block->impl->FuncPtr(block);
,兩種方式實際上均可以拿到FuncPtr
進行調用。
至此,Block的底層最簡單數據結構就初步分析完了。