__block

__ 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,且这个值也出现在结构体实例的初始化中,这意味着该结构体持有相当于原自动变量的成员变量。

你可能感兴趣的:(__block)