Blocks篇:3.Blocks使用捕获到的变量
所谓Blocks捕获变量,即在Block函数体内使用外部声明的变量。
1. 捕获局部变量(自动变量)和静态局部变量
// main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
int uselessVal = 1;
// 局部变量
int val = 3;
// 静态局部变量
static int staticVal = 4;
void (^myBlock)(void) = ^{
// 捕获val和staticVal使用
printf("val = %d\n", val);
printf("staticVal = %d\n", staticVal);
};
myBlock();
}
return 0;
}
转换后的C++代码部分如下:
// main.cpp
/** Block的完整结构体声明 */
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int val; // 内部捕获的局部变量
int *staticVal; // 内部捕获的静态局部变量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int *_staticVal, int flags=0) : val(_val), staticVal(_staticVal) {
impl.isa = &_NSConcreteStackBlock; // 这里只按照MRC编译确定,ARC则不同
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
/** Block中函数体转换生成的C函数 */
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 获取Block结构体实例中捕获的变量
int val = __cself->val; // bound by copy
int *staticVal = __cself->staticVal; // bound by copy
printf("val = %d\n", val);
printf("staticVal = %d\n", (*staticVal));
}
// main函数
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int uselessVal = 1;
int val = 3;
static int staticVal = 4;
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, val, &staticVal)); // 初始化时赋值,将要捕获的变量传递进去
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
return 0;
}
以上可以看出:
- 只有Block内部使用的变量才会被捕获;
-
普通的局部变量是通过值传递方式进行捕获,静态局部变量是通过指针传递方式进行的;
- 原因:
- 由于C函数的声明和实现是在原局部变量的生命周期之外(这里是main函数外部),使用时原变量早已被释放,故需要直接将值保存到Block结构体实例中;
- 对于静态局部变量,由于静态变量是存储在专门的数据区(也就可以理解为全局),在C函数中也可以直接访问,故只需将其指针保存在Block结构体实例中即可。
- 原因:
- C函数内部使用时,通过传递进去的Block指针取出被捕获的变量值。
- 补充:在ARC环境下,对于Block的类型(isa),
- 捕获普通局部变量后,在运行时,系统会将Block拷贝到堆上,变为_NSConcreteMallocBlock
- 捕获静态局部变量,虽然使用,实际也只是记录该变量的指针,Block为_NSConcreteGlobalBlock
- 没有捕获任何变量的Block为_NSConcreteGlobalBlock类型
2. 捕获全局变量和全局静态变量
// main.m
int globalVal = 3;
static int globalStaticVal = 4;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^myBlock)(void) = ^{
printf("globalVal = %d\n", globalVal);
printf("globalStaticVal = %d\n", globalStaticVal);
};
myBlock();
}
return 0;
}
转换后的C++部分代码为:
// main.cpp
int globalVal = 3;
static int globalStaticVal = 4;
/** Block完整结构体声明 */
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;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 直接使用
printf("globalVal = %d\n", globalVal);
printf("globalStaticVal = %d\n", globalStaticVal);
}
可以看到,
- 对于全局变量(包含静态全局变量),由于在Block的C函数中可以直接访问,故无需在Block结构中对其进行额外的保存工作。
- 在ARC环境下,Block的类型(isa)为_NSConcreteGlobalBlock
网友评论