通过这篇博文, 你可以了解到不访问/访问外部变量, 不修改外部变量的Block被转换到C++后的实现方法.
不访问外部变量, 不修改外部变量
void callBlock(void (^myblock)(void)){
myblock();
}
void testBlock(){
void (^testBlock)(void) = ^{
printf("执行block");
};
callBlock(testBlock);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
testBlock();
}
return 0;
}
使用命令行
$ clang -rewrite-objc main.m
被转换到如下代码(只挑选出Block的转换部分)
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
void callBlock(void (*myblock)(void)){
((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
}
struct __testBlock_block_impl_0 {
struct __block_impl impl;
struct __testBlock_block_desc_0* Desc;
__testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
printf("执行block");
}
static struct __testBlock_block_desc_0 {
size_t reserved;
size_t Block_size;
} __testBlock_block_desc_0_DATA = { 0, sizeof(struct __testBlock_block_impl_0)};
void testBlock(){
void (*testBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA));
callBlock(testBlock);
}
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
testBlock();
}
return 0;
}
代码行数增加了很多, 我们一点点的说.
先看最熟悉的
static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
printf("执行block");
}
我们写的block内部执行语句, 最后会转换到这个方法内部. 在看看这个方法是被谁调用的.搜索一下会发现, __testBlock_block_func_0只有在testBlock() 方法中出现过, 我们来看看这个方法
void testBlock(){
void (*testBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA));
callBlock(testBlock);
}
这段代码各种转换的有点乱了, 如果简化一下
void testBlock(){
__testBlock_block_impl_0 *testBlock = &__testBlock_block_impl_0(__testBlock_block_func_0,&__testBlock_block_desc_0_DATA);
callBlock(testBlock);
}
__testBlock_block_func_0 被传到__testBlock_block_impl_0的内部了. 来看看__testBlock_block_impl_0
struct __testBlock_block_impl_0 {
struct __block_impl impl;
struct __testBlock_block_desc_0* Desc;
__testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
impl.FuncPtr = fp;
说明我们的__testBlock_block_func_0被传入到impl->FuncPtr的变量中.
回到void testBlock()
中, 再看第二行 callBlock(testBlock);
void testBlock(){
__testBlock_block_impl_0 *testBlock = &__testBlock_block_impl_0(__testBlock_block_func_0,&__testBlock_block_desc_0_DATA);
callBlock(testBlock);
}
实例化了一个__testBlock_block_impl_0类型变量, 并传递到了callBlock方法中.
void callBlock(void (*myblock)(void)){
((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
}
这段代码简化一下后变成如下
void callBlock(void (*myblock)(void)){
// ((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
*myblock->impl_>FuncPtr(myblock);
}
FuncPtr(myblock)
上文说到, 我们的__testBlock_block_func_0被传入到impl->FuncPtr的变量中. 这一行就调用__testBlock_block_func_0方法, 同时将__testBlock_block_impl_0的实例变量myblock传入方法中.
到此就完成的方法的调用.
访问外部变量, 不修改外部变量
void callBlock(void (^myblock)(void)){
myblock();
}
void testBlock(){
int aaa = 333;
void (^testBlock)(void) = ^{
printf("block 访问外部变量 %d", aaa);
};
callBlock(testBlock);
}
int main(int argc, const char * argv[]) {
testBlock();
return 0;
}
转换之后的代码:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
void callBlock(void (*myblock)(void)){
((void (*)(__block_impl *))((__block_impl *)myblock)->FuncPtr)((__block_impl *)myblock);
}
struct __testBlock_block_impl_0 {
struct __block_impl impl;
struct __testBlock_block_desc_0* Desc;
int aaa;
__testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int _aaa, int flags=0) : aaa(_aaa) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
int aaa = __cself->aaa; // bound by copy
printf("block 访问外部变量 %d", aaa);
}
static struct __testBlock_block_desc_0 {
size_t reserved;
size_t Block_size;
} __testBlock_block_desc_0_DATA = { 0, sizeof(struct __testBlock_block_impl_0)};
void testBlock(){
int aaa = 333;
void (*testBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA, aaa));
callBlock(testBlock);
}
int main(int argc, const char * argv[]) {
testBlock();
return 0;
}
先看下这段代码
void testBlock(){
int aaa = 333;
void (*testBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA, aaa));
callBlock(testBlock);
}
我们访问的外部变量aaa = 333
被传入到__testBlock_block_impl_0中. 再看__testBlock_block_impl_0
struct __testBlock_block_impl_0 {
struct __block_impl impl;
struct __testBlock_block_desc_0* Desc;
int aaa;
__testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int _aaa, int flags=0) : aaa(_aaa) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
对比不访问外部变量的block现在的 __testBlock_block_impl_0 结构体刚好多了一个成员变量aaa. 说明我们访问的外部变量在转换后, 会变成__testBlock_block_impl_0实例的成员变量.
调用的过程与不访问外部变量的步骤基本一直. 这里不在赘述,
static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
int aaa = __cself->aaa; // bound by copy
printf("block 访问外部变量 %d", aaa);
}
这个方法与不访问外部变量方法不太一样, 多了一行int aaa = __cself->aaa;
上文我们提到aaa被传入到__testBlock_block_impl_0结构体实例testBlock中, 通过callBlock方法传递给了__testBlock_block_func_0, 这里的 __cself指的就是testBlock实例.