- 1 新建一个工程在main函数中添加如下代码
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
int i = 2;
void (^myBlock)() = ^ {
printf("i的值是:%d\n",i);
};
i = 3;
myBlock ();
}
return 0;
}
运行结果:i的值是:2
为什么是2 不是3呢?带着这个疑问我们往下看
编译main.m文件,1.打开终端,2.找到工程文件,3.找到main.m文件所在的位置,4.clang -rewrite-objc main.m重新编译会生成一个main.cpp的文件。这个文件相当大滑倒文件底部找到如下代码
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int i = 2;
void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
i = 3;
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
return 0;
}
由上面的代码我们看到Block被转化为了__main_block_impl_0的结构体实例
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
Desc :描述 block 的结构体变量;
i:参数
__main_block_impl_0 :结构体的构造函数,初始化结构体变量 impl、Desc;
impl:__block_impl 是 block 实现的结构体
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
isa:指向实例对象,说明 block 本身也是 Objective-C 对象;
Flags:按位承载 block 的附加信息;
Reserved :保留变量;
FuncPtr:函数指针,指向 Block 要执行的函数,即 __main_block_func_0;
static void __main_block_func_0
// __main_block_func_0 是 block 要最终要执行的函数代码
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int i = __cself->i; // bound by copy
printf("i的值是:%d\n",i);
}
__main_block_impl_0类型的参数__cself在__main_block_func_0内部copy了一份i,我们打印的实际上是新复制的这一份,所以block内部的i和外部的i不是同一份,不论外部i怎么变化,对block内部都没有影响,也就解释了为什么打印结果是2不是3
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)};
reserved :结构体信息保留字段
Block_size :结构体大小
此处已定义了一个该结构体类型的变量 __main_block_desc_0_DATA
- 2 __block
把main函数的代码改为如下
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int i = 2;
void (^myBlock)() = ^ {
printf("i的值是:%d\n",i);
};
i = 3;
myBlock ();
}
return 0;
}
运行结果:i的值是:3
这又是为什么呢?咱们继续往下看
clang -rewrite-objc main.m 打开main.cpp 找到如下代码
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 2};
void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
(i.__forwarding->i) = 3;
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
return 0;
}
我们发现只是在参数i前面加__block,i的定义变复杂了很多,i 是__Block_byref_i_0类型的结构体
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;
};
__isa :指向实例对象,表明i是OC的对象,不再是基本数据类型
__forwarding:指向自己的指针
void (*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
main_block_impl_0 中引用的是 Block_byref_i_0 的结构体指针
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
从__main_block_impl_0结构体看到:i(_i->__forwarding),i的值是通过__forwarding指针取到的, (i.__forwarding->i) = 3;
修改的也是__forwarding指向的Block_byref_i_0结构体中的i,所以打印结果为3
在写的过程中参考了如下文章,谢谢文章作者
block的实现