對于學習過Objective-C的人來說舟扎,一定知道OC中神奇的代碼塊block
庸推,下面我們就來講一講block
朽缴。
Block實際上是作為極普通的C語言源碼來處理的:含有Block語法的源碼首先被轉換成C語言編譯器能處理的源碼蝇完,再作為普通的C源代碼進行編譯洒敏。
使用LLVM編譯器的clang命令可將含有Block的Objective-C代碼轉換成C++的源代碼咏花,以探查其具體實現(xiàn)方式:
clang -rewrite-objc 源碼文件名
我們在Xcode中創(chuàng)建一個Command Line Tool項目趴生,語言選擇Objective-C,代碼如下:
#import <Foundation/Foundation.h>
int (^blk)(int) = ^(int count){
return count+1;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
^{};
blk(1);
}
return 0;
}
經(jīng)過clang轉化后我們會得到一個main.cpp的c++文件打開后發(fā)現(xiàn),這個文件代碼有9萬多行苍匆,前面都是Foundation
框架的代碼刘急,我們不用去在意,將文件移到最后就能看到我們所需要的代碼了浸踩。
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count) {
return count+1;
}
static struct __blk_block_desc_0 {
size_t reserved;
size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};
static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);
int (*blk)(int) = ((int (*)(int))&__global_blk_block_impl_0);
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) {
}
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)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 1);
}
return 0;
}
我們還需要 Foundation
框架中block結構體的定義
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
isa
:指向block的類的指針:block一共有三個類
- _NSConcreteStackBlock:在棧上創(chuàng)建的Block對象
- _NSConcreteMallocBlock:在堆上創(chuàng)建的Block對象
- _NSConcreteGlobalBlock:全局數(shù)據(jù)區(qū)的Block對象
FuncPtr
:指向block實現(xiàn)方法的指針
我們先來看main函數(shù)中的block
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) {
}
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_block_impl_0
是block的完整結構體叔汁,
__block_impl
是一個block的結構體
__main_block_desc_0
是一個block描述的結構體其中
reserved
是保留的字節(jié)數(shù)
Block_size
是block結構體的空間,大小就是sizeof(struct __main_block_impl_0)
其中也有結構體的構造函數(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;
}
main函數(shù)中我們本來寫的^{}
轉化為了((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
一串復雜的代碼民轴,其實就是構造了一個__main_block_impl_0
結構體轉化成代碼就是
__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA,int flag=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = __main_block_func_0;
Desc = __main_block_desc_0_DATA;
}
這樣就定義了一個完整的__main_block_impl_0
結構體攻柠。
下面我們來看下blk
Block的實現(xiàn)。
struct __blk_block_impl_0 {
struct __block_impl impl;
struct __blk_block_desc_0* Desc;
__blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count) {
return count+1;
}
static struct __blk_block_desc_0 {
size_t reserved;
size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};
static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);
int (*blk)(int) = ((int (*)(int))&__global_blk_block_impl_0);
大致和上一個block相同后裸,不過blk是一個全局的block瑰钮,定義的時候也在main
方法外面,isa
指針的值為&_NSConcreteGlobalBlock
調(diào)用blk時代碼為
((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 1);
從中可以看見調(diào)用的就是__block_impl
結構體的FuncPtr
是一個void指針微驶,指向的就是static int __blk_block_func_0(struct __blk_block_impl_0 *__cself, int count)
這個靜態(tài)方法浪谴。
到這里,我們就能理解block是如何實現(xiàn)的了因苹。
總結一下苟耻,block在c語言中是以結構體的方法存儲的,在Objective-C
中是當做對象來使用的扶檐,block有三種不同的類型凶杖,分別是全局block,棧block和堆block款筑。