Block僅用作輸出語句的情況
</br>
int main() {
void (^blk)(void) = ^{ printf("Block\n"); };
blk();
return 0;
}
轉(zhuǎn)換為C++后:
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 {
unsigned long reserved;
unsigned long Block_size;
} __main_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0);
};
int main() {
void (*blk)(void) = (void(*)(void)) &__main_block_impl_0( (void *)__main_block_func_0, &__main_block_desc_0_DATA);
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr) ((struct __block_impl *)blk);
return 0;
}
轉(zhuǎn)換后代碼量激增,不要急著驚詫。
main()
函數(shù)前面的一大堆代碼只不過是結(jié)構(gòu)體的定義而已傲隶,我們看到最最主要的main()
函數(shù)并沒有發(fā)生多大的改變喊括,轉(zhuǎn)換前后都是3條語句而已胧瓜,所以先從main()
函數(shù)切入。
</br>
1.1 main函數(shù)第一條語句分析
</br>
為了便于觀察分析郑什,先把“長且臭”的類型轉(zhuǎn)換去掉:
void (^blk)(void) = ^{ printf("Block\n"); };
//vs
void (*blk)(void) = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA);
?可以發(fā)現(xiàn)府喳,代碼塊不過是一個(gè)函數(shù)的指針而已。那右邊賦值給它的必然就是一個(gè)函數(shù)的地址了:
&__main_block_impl_0(para1, para2);
沒錯(cuò)吧蘑拯?這個(gè)函數(shù)是結(jié)構(gòu)體__main_block_impl_0
的構(gòu)造函數(shù)劫拢。
根據(jù)名字,我們還可以推斷出這個(gè)結(jié)構(gòu)體的意義:這個(gè)結(jié)構(gòu)體是為了實(shí)現(xiàn)在main()
函數(shù)中定義的?Block强胰,在此例是blk
舱沧。末尾追加數(shù)字0,用以標(biāo)識不同的Block偶洋。
既然說到構(gòu)造函數(shù)了熟吏,那自然要看看這個(gè)結(jié)構(gòu)體的成員變量,里面都有什么:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0 *Desc;
};
impl變量展開后:
Block
里面含有void *isa
成員變量玄窝,可見Block是對象牵寺,而且一般來說它是分配在棧中的對象。
void *isa
是什么恩脂?可瀏覽筆者另文:《Objective-C類的實(shí)質(zhì)是結(jié)構(gòu)體》下文也會有粗略說明帽氓。
下面看構(gòu)造函數(shù)是怎么給這些成員變量賦值的:
__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;
};
void (*blk)(void) = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA);
-
isa
指針,用于說明該blk
實(shí)例(因其也是對象俩块,可以說成是Block實(shí)例)是什么“Block類”黎休,類似于說明
student
是Student
類一樣(Student *student = [Student alloc] init];
)∮窨總共有3
種Block類:
改自《Objective-C高級編程iOS與OS X多線程和內(nèi)存管理 表2-3 Block的類》 -
Flags
略 -
Reserved
略 -
FuncPtr
一個(gè)函數(shù)指針漫仆,指向Block的實(shí)現(xiàn)代碼捎拯,即^{... }
部分,上例中是^{ printf("Block\n") ;}
-
Desc
為descriptor
縮寫盲厌。是指向存放?代碼塊描述符的指針署照。里面有個(gè)Block_size
變量,聲明了塊對象的總體大小吗浩。
雖然Block還可能包含其他一些上面未列出的屬性建芙,但上面的屬性是每個(gè)Block都有的屬性。
接著拓萌,觀察FuncPtr指向的Block實(shí)現(xiàn)代碼:
static void __main_block_func_0 (struct __main_block_impl_0 *__cself) {
printf("Block\n");
}
__cself
指針指向塊的結(jié)構(gòu)體__main_block_impl_0
變量岁钓,所以__cself
與C++
中的this
、Objective-C
中的self
類似微王。
</br>
1.2 main函數(shù)第二條語句分析
blk();
//vs
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr) ((struct __block_impl *)blk);
“長且臭”去掉后:
(*blk->impl.FuncPtr)(blk);
前文已述屡限,FuncPtr
是一個(gè)函數(shù)指針,指向Block的實(shí)現(xiàn)代碼炕倘,所以(*blk->impl.FuncPtr)()
是函數(shù)指針調(diào)用函數(shù)(即Block的實(shí)現(xiàn)代碼)钧大,其中傳入了參數(shù)blk
,blk
作為參數(shù)傳入__cself
罩旋。
題外話:blk
實(shí)例準(zhǔn)確來說應(yīng)該是一個(gè)struct __main_block_impl_0 *
類型啊央,在使用blk
時(shí),卻將其強(qiáng)制轉(zhuǎn)換成struct __block_impl *
涨醋,使外部代碼只能訪問blk
以struct __block_impl *
為身份的那些變量瓜饥,從而對塊描述符Desc
和構(gòu)造函數(shù)進(jìn)行了封裝。體現(xiàn)了封裝性浴骂。