block是如何实现的
我们通过clang编译出Objective-C文件对应cpp文件,去看他的实现方式,对比不同样式的block
的不同,
从中得到它的实现思想
无参数Block的基本实现
#import "ViewController.h"
typedef void(^WxsBlock) ();
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
WxsBlock blockObj = ^(){
NSLog(@"test");
};
blockObj();
}
@end
我们在ViewController.m中声明,实现并使用一个不带参数的Block
然后通过clang编译出对应的cpp文件,我们只关心和Block对应的代码,如下
typedef void(*WxsBlock) ();
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_dd8c22_mi_0);
}
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
WxsBlock blockObj = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)blockObj)->FuncPtr)((__block_impl *)blockObj);
}
我们来解析一下
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
}
这个结构体的作用相当于是一个block的“核心功能类”,包含指向superclass的指针,
执行方法的指针。
void *isa
指向superclass的指针,在Objective-C中Block也是对象,这个就不难理解了int Flags
标记,这个不知道有什么深意,暂且我们理解为做一个标记的作用。int Reserved
预留字段,现在不必关心,不过这种预留的思想可以学习void *FuncPtr
要执行方法的指针,这个是用来持有block代码块中方法的指针
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
__ViewController__viewDidLoad_block_desc_0
看名字,这是一个用来描述block的,
size_t reserved
预留的空间大小size_t Block_size
block的空间大小__ViewController__viewDidLoad_block_desc_0_DATA
声明的结构体变量,参数是reserved = 0
Block_size
是__ViewController__viewDidLoad_block_impl_0
的大小
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
}
看到impl这个应该是对应Implements,见名知意我们可以理解为声明一个block对应的类
struct __block_impl impl
就是我们上边描述的“核心类”struct __ViewController__viewDidLoad_block_desc_0* Desc
block的描述,主要描述block所占空间大小Block_size__ViewController__viewDidLoad_block_impl_0
是此结构体的一个构造函数void *fp
构造函数第一个参数,执行函数指针,赋值给impl
变量的funcPtr
指针struct __ViewController__viewDidLoad_block_desc_0 *desc
这里又作为构造函数的第二个参数,赋值给Desc
变量int flags=0
同上,赋值给impl
中的Flags
变量,但这有趣的是,它默认为0,深意自咎,因为我也猜不出来。。impl.isa = &_NSConcreteStackBlock;
这个对应的还有_NSConcreteGlobalBlock
_NSConcreteMallocBlock
这里我们只需要知道它对应的是在栈上的block,其他的我们后边解析
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_dd8c22_mi_0);
}
static void __ViewController__viewDidLoad_block_func_0
静态block执行函数,也就是我们在{}
中的NSLog
那一段。struct __ViewController__viewDidLoad_block_impl_0 *__cself)
参数是我们刚才提到的impl
“核心功能类”,但是奇怪的是它并没有在函数体中使用,
这个我们看后边能否解答。- 中间的就不用说了,NSLog。。
WxsBlock blockObj = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
(void (*)())
标示无返回值的匿名函数指针,并且函数没有参数,这也正对应了我们前边的typedef void(*WxsBlock) ();
block声明&
这个取地址符我有点懵逼,来个懂C的大神指点一下(ps:默默的去看看大学的C课本。。),虽然看不懂,但是不影响我们的整体解读。__ViewController__viewDidLoad_block_impl_0
是__ViewController__viewDidLoad_block_impl_0
结构体的一个构造函数,也就是我们定义的block的一个构造函数。参数上边有说。__ViewController__viewDidLoad_block_func_0
里边是NSLog...
的那个函数指针,也就是我们在block体中写的代码逻辑指针。__ViewController__viewDidLoad_block_desc_0_DATA
是__ViewController__viewDidLoad_block_desc_0
声明是创建的一个描述对象
带参数Block实现
在.m文件中:
typedef void(^WxsBlock) (NSString *str,NSArray *arr);
- (void)viewDidLoad {
[super viewDidLoad];
WxsBlock blockObj = ^(NSString *str,NSArray *arr){
NSLog(@"test");
};
blockObj(@"wxs",[NSArray array]);
}
通过clang编译后:
typedef void(*WxsBlock) (NSString *str,NSArray *arr);
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself, NSString *str, NSArray *arr) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_cf62af_mi_0);
}
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
WxsBlock blockObj = ((void (*)(NSString *, NSArray *))&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
((void (*)(__block_impl *, NSString *, NSArray *))((__block_impl *)blockObj)->FuncPtr)((__block_impl *)blockObj, (NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_cf62af_mi_1, ((NSArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array")));
}
通过对比看出,我们加入了两个参数,编译出来的cpp代码和之前的对照也只是在传参的地方有所不同,在实现和逻辑调用上并没有发生变化。