__ block
^{printf(fmt, val)}
//该源码转换结果如下
static void __main_block_func_0(srtuct __main_block_impl_0 *__cself){
const char *fmt = __cself->fmt;
int val = __cself->val;
printf(fmt, val);
}
Block中所使用的被截获自动变量就如“带有自动变量值的匿名函数”所说,仅截获自动变量的值,Block中使用自动变量后,在Block的结构体实例中重写该自动变量也不会改变原先截获的自动变量。
int val = 0;
void (^blk) (void) = ^{val = 1;};
如前所述,因为在实现上不能改写被截获自动变量的值,所以当编译器在编译过程中检出给被截获自动变量赋值的操作时,变产生编译错误。
我们来看看下面这段代码:
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;
}
//该源码使用了Block改写静态变量static_val,静态全局变量static_global_val和全局变量global_val。该源码转换后如下:
int global_val = 1;
static int static_global_val = 2;
srtuct __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_global_val和全局变量global_val的访问与转换前完全相同。静态变量static_val又要如何转换呢?
static void __main_block_func_0(srtuct __main_block_impl_0 *__cself) {
int *static_val = __cself->static_val;
(*static_val) *= 3;
}
使用静态变量static_val的指针对其进行访问,将静态变量static_val的指针传递给__main_block_impl_0结构体的构造函数并保存。这时超出作用域使用变量的最简单方法。
静态变量的这种方法似乎也适用于自动变量的访问。但是我们没有这样做。
实际上,在由Block语法生成的值Block上,可以存有超过其变量作用域的被截获对象的自动变量,变量作用域结束的同时,原来的自动变量被废弃,因此Block中超过变量作用域而存在的变量同静态变量一样,将不能通过指针访问原来的自动变量。
解决block中不能保存值这一问题的第二种方法是使用“__block说明符”.更准确的表述方式为"__block存储域类说明符"( __ block storage-class-specifier)。
__ block说明符类似于static,auto和register说明符。它们用于指定将变量值设置到哪个存储域中。
__block int val = 10;
void (^blk)(void) = ^(val = 1);
该源代码可进行编译,转换后如下:
srtuct __Block_byref_val_0 {
void *__isa;
__Block_byref_val0 *__forwarding;
int __flags;
int __size;
int val;
};
struct __main_block_impl_0
{
srtuct __block_impl impl;
srtuct __main_block_desc_0* Desc;
__Block_byref_val_0 *val;
__main_block_impl_0(void *fp, srtuct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(static __main_block_impl_0 *__cself) {
__Block_byref_val_0 *val = __cself->val;
(val->__forwarding->val) = 1;
}
............
只是在自动变量上加了__ block说明符,源代码就急剧增加
__block int val = 10;
//这个__block变量val是怎样转换过来的呢?
__Block_byref_val_0 val = {
0,
&val,
0,
sizeof(__Block_byref_val_0),
10
};
我们发现,它竟然变为了结构体实例。__ block变量也同Block一样变成__ Block _ byref _ val _ 0结构体类型的自动变量,即栈上生成的__ Block _ val _ 0结构体实例。该变量初始化为10,且这个值也出现在结构体实例的初始化中,这意味着该结构体持有相当于原自动变量的成员变量。