大家都知道 Block是“带有自动变量的匿名函数” 但究竟是什么,通过一个简单的block实例来说明。
int main(){
void (^blk) (void) = ^{ char a = 'A'; };
blk();
return 0;
}
这是一个简单的block函数的定义及调用,我们通过clang 可以将上面的代码转换成下面的形式
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr; //函数指针
};
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)}; //初始化
struct __main_block_impl_0 {
struct __block_impl impl; // __block_impl结构体类型的变量
struct __main_block_desc_0* Desc; //__main_block_desc_0 结构体类型的指针变量
//__main_block_impl_0结构体 构造函数
__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;
}
};
//相当于源代码中的 ^{ char a = 'A'; };
/*
__cself 是指向自身的 结构体类型(__main_block_impl_0)的指针变量
*/
//转换成的c语言方法
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
char a = 'A'; }
int main(){
void (*blk) (void) = ((void (*)())&__main_block_impl_0 (__main_block_func_0, &__main_block_desc_0_DATA));
// 上面这句代码 就相当于把结构体__main_block_impl_0初始化f然后把 地址赋值给 blk 这个指针变量
/*
*/
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
// 上面这句代码 去掉转换部分 就变成 *blk -> funcPtr(blk) 这是c语言中简单的 函数指针调用函数
return 0;
}
由于转换后代码过多这里只粘贴处有用部分。在上面的代码中也简单写了一下注释。下面来梳理一下期具体流程。
//oc 代码
void (^blk) (void) = ^{ char a = 'A'; };
//转换后C代码
void (*blk) (void) = ((void (*)())&__main_block_impl_0 (__main_block_func_0, &__main_block_desc_0_DATA));
//去掉转换部分 也就是将__main_block_impl_0 的结构体实例 赋值给 __main_block_impl_0的指针变量 *blk
也就是相当于
struct __main_block_func_0 *blk=__main_block_impl_0 (__main_block_func_0, &__main_block_desc_0_DATA);
然后我们再往下一步看,这个struct __main_block_func_0又是什么呢,转换后的代码也给出来,因为在C语言中在使用函数和变量时必须要先定义所以这个结构体在函数调用之前。
struct __main_block_impl_0 {
struct __block_impl impl; // __block_impl结构体类型的变量
struct __main_block_desc_0* Desc; //__main_block_desc_0 结构体类型的指针变量
//__main_block_impl_0结构体 构造函数
__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;
}
};
可以看到抛去结构体本身的构造函数以外,这个结构体只有两个成员,而且这两个成员有都是结构体类型。而这两个结构体在上面也有定义分别是
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr; //函数指针
};
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)}; //初始化
在上面也已经说过了block的定义也就相当于对 __main_block_impl_0结构体实例的初始化。在初始化时传递的两个参数分是 __main_block_func_0 C函数的地址,和__main_block_desc_0_DATA
__main_block_desc_0(结构体)实例的地址。然后在__main_block_impl_0初始化函数中有对他的两个成员进行了赋值。如果根据初始化传递的参数我们将将__main_block_impl_0中__block_impl结构体成员中的元素直接在__main_block_impl_0结构体中定义__main_block_impl_0将变成这样
struct __main_block_impl_0 {
void *isa;
int Flags;
int Reserved;
void *FuncPtr; //函数指针
struct __main_block_desc_0 *desc
};
然后它的初始化函数也将变成
__main_block_impl_0 (__main_block_func_0, &__main_block_desc_0_DATA);
//__main_block_impl_0结构体 构造函数
__main_block_impl_0 {
isa = &_NSConcreteStackBlock;
Flags = 0;
Reserved = 0;
FuncPtr = __main_block_func_0;
Desc = &__main_block_desc_0_DATA;
}
这也就是block的定义了,下面我们来看block的调用。
//原函数
blk();
//转换后
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
// 上面这句代码 去掉转换部分 就变成
*blk -> funcPtr(blk) //这是c语言中简单的 函数指针调用函数,参数就是__main_block_impl_0结构体的指针变量。
到这里也就摸清了block的实质了。最后我们看一下__main_block_impl_0结构体中的
isa = &_NSConcreteStackBlock;
这是将_NSConcreteStackBlock的指针(地址)赋值给了isa,那_NSConcreteStackBlock是什么呢?我们都知道在OC中,类和实例也都是 结构体类型的,而且在改结构体实例当中 也都有isa指针,成员变量,方法名,方法实现(也就是函数指针)以及属性。
现在再看__main_block_impl_0结构体,它就相当于基于Objective—C类对象的结构体。_NSConcreteStackBlock 也就相当于 class_t 结构体实例。在将Block作为Objective—C的对象处理时,将关于该类的信息就放在_NSConcreteStackBlock中。
现在也就知道了block的实质也就是Objective—C的对象。