int val = 0 ; void (^blk)(void) = ^{ val = 1; };
以上代码编译会报错,因为blocks里面尝试修改val变量值。(编译的过程中,val已经超出其作用域,blocks里面保存的时截获的瞬间值)
我们可以使用静态变量,这样在blocks也可以访问得到外面的变量值。
例如:
int global_val = 1; static int static_global_val= 2; int main () { static int static_val =3; void (^blk)(void) = ^{ global_val *= 1; static_global_val *= 2; static_val *=3; }; return 0; }
转换之后为:
int global_val = 1; static int static_global_val= 2; struct __block_impl { void * isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0 *Desc; int *static_val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags = 0):static_val(_static_val) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_func_0 (struct __main_block_impl_0 *__cself) { int *static_val = __cself ->static_val; global_val *= 1; static_global_val *= 2; (*static_val) *= 3; } static struct __main_block_desc_0 { unsigned long reserved; unsigned long Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0) }; int main() { static int static_val = 3; blk = & __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &static_val); return 0; }
以上的做法是使用静态变量保存指针,然后在blocks的实现中通过指针改变静态变量值。
另外可以使用 __block 说明符。
例如:
__block int val = 1; void (^blk)(void) = ^{ val = 1; };
转换为:
struct __block_impl { void * isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_byref_val_0 { void *__isa; __Block_byref_val_0 *__forwarding; int __flags; int __size; int val; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0 *Desc; __Block_byref_val_0 *val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *val, int flags = 0):val(i_val->__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_val_0 *val = __cself ->val; val->__forwarding->val = 1; } static void __main_block_copy_0 (struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src) { _Block_object_assign (&dst->val, src->val, BLOCK_FIELD_IS_BYREF); } static void __main_block_dispose_0(struct __main_block_imp1_0 *src) { _Block_object_dispose(src->val, BLOCK_FIELD_IS_BYREF); } static struct __main_block_desc_0 { unsigned long reserved; unsigned long 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), __main_block_copy_0; __main_block_dispose_0; }; int main() { __Block_byref_val_0 val ={0, &val, 0, sizeof(__Block_byref_val_0), 10}; static int static_val = 3; blk = & __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &val, 0x22000000); return 0; }
① 由上可知,__block 修饰的int变量,变成了结构体实例。即__Block_byref_val_0 的自动变量。
② __Block_byref_val_0 结构体中的__forwarding 指针式指向自身的指针。
③ __Block_byref_val_0 结构体不在__main_block_impl_0 结构体中,这样做是为了在多个Block中使用 __block 变量。
所谓结构体类型的自动变量,即栈上生成的该结构体的实例。
类 | 设置对象的存储域 |
_NSConcreteStackBlock | 栈 |
_NSConcreteGlobalBlock | 程序的数据区域(.data区) |
_NSConcreteMallocBlock | 堆 |
即使在函数内而不再记述广域变量的地方使用Block语法时,只要Block不截获自动变量值,就可以将Block用结构体实例设置在程序的数据区域。
① 记述全局变量的地方有 Block 语法时。
② Block 语法的表达式中不是用应截获的自动变量时。
以上两种情况,Block 为 _NSConcreteGlobalBlock 类对象。即Block 配置在程序的数据区域中。
① Block 超出变量作用域可存在的原因
② __block 变量用结构体成员变量 __forwarding 存在的原因
将Block 和__block 变量从栈上复制到堆上的方法来解决当栈超出作用域而被废弃的问题。【_NSConcreteMallocBlock】
__forwarding 指针 实现了无论 __block变量配置在栈上还是堆上都能够正确访问 __block 变量。
【Objective-C高级编程】iOS与OS X多线程和内存管理(六) __block 说明符,布布扣,bubuko.com