block 又称之为“自带变量的匿名函数”,抛开OC语法定义block的形式不谈,其实好多语言都有类似的函数,比如JS的回调函数(其实就是将一个匿名还是作为函数的实参)、swift的闭包等等。。
首先讲一下oc block的实质,通过自身的理解,加以各位大神的剖析文章。block 在编译时期会被编译成结构体,也就是说OC的block底层是使用C语言结构体实现的, 和对象、类的实现是一样的(所以其实block就是OC中的一个对象),这个结构体包括两个结构体成员变量和一个构造函数,我们都知道Objective-C是用OC实现底层的,所以我们需要将代码转化为底层的实现(使用Clang -rewrite-objc [文件名], 这个命令是将OC语言转化成C或者C++的代码,经常使用这个命令我们窥探OC的底层奥秘)
.m 源码
#import <Foundation/Foundation.h>
int main(int argc, char * argv[]) {
void (^myBlock)(void) = ^{
NSLog(@"hello world");
};
myBlock();
return 0;
}
clang 转化成的过程源码
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_dr_jkts5c395zs9xcx0bt_r7fx40000gn_T_main_7d68a8_mi_0);
}
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, char * argv[]) {
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
return 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;
}
};
上面的源码转换成C代码之后,我做了一次完全的剖析,我们可以得出的结论就是,block 的实现其实就是一个结构体,而其的调用则是调用结构体内的函数指针调用函数;下面我将稍微改动一下代码,看一下生成的底层代码,研究一下,截获的外部变量的情况:
我只将不同的代码提出来
.m源代码
int main(int argc, char * argv[]) {
int a = 10;
void (^myBlock)(void) = ^{
NSLog(@"hello world %d", a);
};
myBlock();
return 0;
}
clang 转换C代码
int main(int argc, char * argv[]) {
int a = 10;
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
return 0;
}
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_dr_jkts5c395zs9xcx0bt_r7fx40000gn_T_main_3e8c5a_mi_0, a);
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
那么我们可以看出,再没有任何修饰符的情况下,block截获的变量只是一个值,而并不是指针,所以,内部执行函数无法改变block外部的变量
通过上边的例子,我们可以猜想,如果block的结构体如果可以拥有变量的指针那么,就可以修改外部变量的值了 ,所以 _ block就是做这件事情的,其实_ block的实现远比我们想象的复杂,我们看一下__block 修饰之后转换的代码:
int main(int argc, char * argv[]) {
int a = 10;
int b = 20;
__attribute__((__blocks__(byref))) __Block_byref_c_0 c = {(void*)0,(__Block_byref_c_0 *)&c, 0, sizeof(__Block_byref_c_0), 30};
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, b, (__Block_byref_c_0 *)&c, 570425344));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
return 0;
}
struct __Block_byref_c_0 {
void *__isa;
__Block_byref_c_0 *__forwarding;
int __flags;
int __size;
int c;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
int b;
__Block_byref_c_0 *c;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int _b, __Block_byref_c_0 *_c, int flags=0) : a(_a), b(_b), c(_c->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_c_0 *c = __cself->c;
int a = __cself->a;
int b = __cself->b;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_dr_jkts5c395zs9xcx0bt_r7fx40000gn_T_main_55b164_mi_0, a, b, (c->__forwarding->c));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->c, (void*)src->c, 8
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->c, 8
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0),
所以我们可以看得出来_ block 的作用是生成外部变量的指针,从而达到在block内部可以修改的目的;下一篇文章将会结合这篇文章Block的本质,总结_ weak _ block _ strong的使用。
致谢:https://blog.csdn.net/abc649395594/article/details/47086751
https://www.jianshu.com/p/fdd7fa9a9e7e
两篇文章的对我的帮助!