Objective-C 之Block(3)

__block 说明符

在Block中无法给Block外的自动变量进行赋值,解决这个问题有两个方法

  1. C语言的静态变量/静态全局变量/全局变量,允许Block改写值。
  2. 使用__block说明符

第一种代码如下:

int global_val = 1;

static int static_global_val = 2;

int main(int argc, char * argv[]) {
    
    static int static_val = 3;
    void (^blk)(void) = ^{
        global_val *=1;
        static_global_val *=2;
        static_val *=3;
    };
    
    return 0;
}

复制代码

转换后的代码如下:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

int global_val = 1;

static int static_global_val = 2;


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; // bound by copy

        global_val *=1;
        static_global_val *=2;
        (*static_val) *=3;
    }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {

    static int static_val = 3;
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));

    return 0;

}

复制代码

全局变量和静态全局变量与转换前完全相同,静态变量static_val转换后使用静态变量的指针对其进行访问。将静态变量static_val的指针传递给__main_bock_impl_0结构体的构造函数并保存。

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy

        global_val *=1;
        static_global_val *=2;
        (*static_val) *=3;
    }
复制代码

实际上,在由Block语法生成的值Block上,可以存有超过其比变量作用域的被截获对象的自动变量。在变量作用域结束的同时,原来的自动变量被废弃,因为Block中超过变量作用域而存在的变量同静态变量一样,将不能通过指针访问原来的自动变量。

第二种方法:使用__block 说明符 (__block存储域类说明符) C语言中有以下存储域类说明符:

  • typedef
  • extern
  • static
  • auto
  • register

引用C语言存储类、链接和内存管理总结一文来解释存储域类说明符

一、存储类 描述一个变量(一个数据对象):存储周期、作用域、链接

作用域:一个C变量的作用域可以是代码块作用域(在函数内部代码块中定义的变量、形参,称为局部变量),函数原型作用域(从变量定义处遗址到原型声明的末尾),或者文件作用域(一个在所有函数之外定义的变量,从定义处到包含定义的文件结尾处都可见,成为全局变量),函数作用域(只适应goto语句使用的标签),函数作用域意味着一个特定函数中的goto标签对该函数中任何地方的代码都是可见的,无论该标签出现在哪一个代码块中。

链接:一个C变量具有一下链接之一,外部链接、内部链接或空链接。

  • 具有代码块作用域和函数原型作用域的变量是空链接,由定义所在的代码块和函数原型所私有
  • 具有外部链接的变量可以在多文件程序的任何文件使用,多个文件可有共享。
  • 具有内部链接的变量可以在一个文件的任何地方使用,归该文件私有。
  • 具有文件作用域的变量可能是内部链接或者外部链接。
  • 如果一个具有文件作用域的变量用static修饰,则是内部链接,否则是外部链接。

存储时期:一个C变量具有以下两种存储时期之一,静态存储时期(在程序执行期间一直存在)和自动存储时期

  • 静态存储时期:在程序执行期间一直存在。具有文件作用域(不管是内部链接还是外部链接)的变量具有此存储周期,注意对于具有文件作用域的变量,static表明链接类型不是存储时期。
  • 自动存储时期:具有代码块作用域的变量一般是自动存储周期。
存储类时期作用域链接声明方式
自动自动代码块空链接代码块内
寄存器自动代码块空链接代码块内,使用关键字register
具有外部链接的静态静态文件外部链接所有函数之外
具有内部链接的静态静态文件内部链接所有函数之外,使用关键字static
空链接的静态静态代码块代码块内,使用关键字static

自动变量:用auto修饰或者不修饰就默认属于自动存储类的变量具有自动存储周期、代码块作用域、空链接。在默认下,在代码快或者函数的头部定义的任意变量都属于自动存储类。

如果在内层中使用和外层一样名字的变量,则内层会覆盖外层变量,在程序离开内层代码块时,外部变量就恢复了其作用和之前的值。

对于自动变量,除非显示的初始化,否则不会自动初始化。

寄存器变量:通过register声明,放在寄存器而非内存中,所以无法获得其地址。注意,有时候虽然请求了把变量放于寄存器中,但是由于寄存器的个数,并不一定能满足,所以寄存器变量就成为了普通变量,不过依然不能对其取址。

具有代码块作用域的静态变量:通过static修饰,如果不现实对其初始化,则自动初始化为0(提供了静态时期),在代码块内声明(提供了代码块作用域和空链接),创建具有代码块作用域兼具静态存储的局部变量。这些变量具有代码块作用域,空链接,静态存储时期。变量一旦被定义便一直存在直到程序结束。

具有外部链接的静态变量:具有文件作用域、外部链接、静态时期(外部存储类,外部变量)。 额外说明,外部变量只可进行一次初始化,而且在定义的时候。

具有内部链接的静态变量:用static在所有函数的外部进行定义来创建,静态存储时期、文件作用域、内部链接。

二、存储类说明符 auto 标明一个变量具有自动存储时期,该说明符只能用在具有代码块作用域的变量声明中。

register 只能用在具有代码块作用域的变量。请求一个变量存储在寄存器中一边快速使用,但是不能获得改变量的地址。

static 用于具有代码块作用域的变量声明时,使该变量具有静态存储时期,从而得以在程序运行期间存在并保留其值,变量仍保留代码块作用域和空链接。若用于具有文件作用域的变量声明时,使该变量具有内部链接。

extern 表明在声明一个在其他地方定义了的变量。

const 将数据定为不变的,在只能定义声明,以后不可改变其值。在指针中使用时,const的位置决定是指针本身不变还是指针指向的数据不变。

volatile 说明数据除了可以被程序修改外还可以被其他代理修改,主要用于编译器优化。

restrict 只能用于修饰指针,限定的指针被认为是提供了对其所在指向的数据块的唯一访问途径。


__blocks说明符类似于static、auto、register,它们用于指定将变量值设置到哪个存储域中。例如auto表示作为自动变量存储在栈中,static表示作为静态变量存储在数据区中。

按照之前提供的__block说明符的代码:

#include <stdio.h>
int main(int argc, char * argv[]) {
    
    __block int val = 10;
    void (^blk)(void)=^{val=1;};
}

复制代码

转换后的代码:

struct __Block_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; // by ref
  __main_block_impl_0(void *fp, struct __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(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref
(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((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t 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(int argc, char * argv[]) {

    __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10};
    void (*blk)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));


}
复制代码

其中

__block int val = 10;

复制代码

转换成了:

__attribute__((__blocks__(byref))) __Block_byref_val_0 val = {
	(void*)0,
	(__Block_byref_val_0 *)&val, 
	0, 
	sizeof(__Block_byref_val_0), 
	10
};
复制代码

即__Block_byref_val_0结构体的一个实例。该结构体声明如下:

struct __Block_byref_val_0 {
  void *__isa;
__Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};
复制代码

在main函数中给val赋值为1的过程转换成为:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

	__Block_byref_val_0 *val = __cself->val; 
	// bound by ref
	
	(val->__forwarding->val)=1;
	
}

复制代码

Block的__main_block_impl_0结构体实例持有指向__block变量的__Block_byref_val_0结构体实例的指针。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  

  __Block_byref_val_0 *val; // by ref
  
  //__main_block_impl_0结构体实例持有指向__block变量的__Block_byref_val_0结构体实例的指针。
  
  
  __main_block_impl_0(void *fp, struct __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;
  }
};

复制代码

__Block_byref_val_0结构体实例的成员变量__forwarding持有指向该实例自身的指针。通过成员变量__forwarding访问成员变量val。(成员变量val是该实例自身持有的变量,它相当于原来的自动变量)。

另外,__block变量的__Block_byref_val_0结构体并不在Block用__main_block_impl_0结构体中,这样做是为了在多个Block中使用__block变量。

int main(int argc, char * argv[]) {
    
    __block int val = 10;
    void (^blk0)(void)=^{val=0;};
    void (^blk1)(void)=^{val=1;};
    
}

复制代码

转换后的代码:

int main(int argc, char * argv[]) {

    __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10};
    void (*blk0)(void)=((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));
    void (*blk1)(void)=((void (*)())&__main_block_impl_1((void *)__main_block_func_1, &__main_block_desc_1_DATA, (__Block_byref_val_0 *)&val, 570425344));

}


复制代码

两个Block都是用了__Block_byref_val_0结构体实例val的指针。这样,就可以从多个Block中使用同一个__block变量。也可以从一个Block中使用多个__block变量,只要增加Block结构体成员变量与构造函数的参数,便可对应使用多个__block变量。


Block存储域

Block转换为Block的结构体类型的自动变量,__block变量转换为__block变量的结构体类型的自动变量。所谓结构体类型的自动变量,即栈上生成的该结构体的示例。

名称实质
Block栈上Block的结构体实例
__block变量栈上__block变量的结构体实例

另外,通过以下转换后的代码可知,Block也是Objective-C对象。将Block当作OC对象来看时,Block的类为_NSConcreteStackBlock。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_val_0 *val; // by ref
  __main_block_impl_0(void *fp, struct __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;
  }
};
复制代码

类似的类有:

  • _NSConcreteGlobalBlock
  • _NSConcreteStackBlock
  • _NSConcreteMallocBlock
设置对象的存储域
_NSConcreteStackBlock
_NSConcreteGlobalBlock程序的数据区域(.data区)
_NSConcreteMallocBlock

一个由C/C++编译的程序占用的内存分为以下几个部分 :

  1. 栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
  2. 堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
  3. 全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
  4. 文字常量区:常量字符串就是放在这里的,程序结束后由系统释放
  5. 程序代码区—存放函数体的二进制代码。

记述在全局变量的地方使用Block语法时,生成的Block为_NSConcreteGlobalBlock类对象。

例如:

void (^blk)(void) = ^{printf("Global Block\n");};

int main(int argc, char * argv[]) {
    
}

复制代码

转换后:

struct __blk_block_impl_0 {
  struct __block_impl impl;
  struct __blk_block_desc_0* Desc;
  __blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteGlobalBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
复制代码

该Block的类为_NSConcreteGlobalStack类。此Block即该Block用结构体实例设置在程序的数据区域中。因为在使用全局变量的地方不能使用自动变量,所以不存在对自动变量进行截获。由此Block用结构体实例的内容不依赖于执行时的状态,所以整个程序中需要一个实例。因此将Block用结构体实例设置在与全局变量相同的数据区域中即可。

只在截获自动变量时,Block用结构体实例截获的值才会根据执行时的状态变化。

例如,以下代码中,虽然多次使用一个Block语法,但是每个循环中截获的自动变量的值都不同。

int main(int argc, char * argv[]) {
    
    typedef int (^blk_t)(int);
    
    for (int rate = 0;rate<10;++rate){
        blk_t blk = ^(int count){
            return rate*count;
        };
    }
    
}
复制代码

转换后:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int rate;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _rate, int flags=0) : rate(_rate) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static int __main_block_func_0(struct __main_block_impl_0 *__cself, int count) {
  int rate = __cself->rate; // bound by copy

            return rate*count;
        }


int main(int argc, char * argv[]) {

    typedef int (*blk_t)(int);

    for (int rate = 0;rate<10;++rate){
        blk_t blk = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, rate));
    }

}
复制代码

而以下代码中在不截获自动变量时,Block用结构体实例每次截获的值都完全相同。

int main(int argc, char * argv[]) {
    
    typedef int (^blk_t)(int);
    
    for (int rate = 0;rate<10;++rate){
        blk_t blk = ^(int count){
            return count;
        };
    }
    
}

复制代码

转换后:

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 int __main_block_func_0(struct __main_block_impl_0 *__cself, int count) {

            return count;
        }
        
int main(int argc, char * argv[]) {

    typedef int (*blk_t)(int);

    for (int rate = 0;rate<10;++rate){
        blk_t blk = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    }

}

复制代码

也就是说,即使在函数内而不在记述广域变量的地方使用Block语法时,只要Block不截获自动变量,就可以将Block用结构体实例设置在程序的数据区域。

虽然通过clang转换的代码是_NSConcreteStackBlock类对象,但是实现上却不同。

  • 记述全局变量的地方有Block语法时
  • Block语法的表达式中不使用应截获的自动变量时

以上情况,Block为_NSConcreteGlobalBlock类对象。即Block配置在程序的数据区域中。除此之外的Block语法生成的Block为_NSConcreteStackBlock类对象,且设置在栈上。

而_NSConcreteMallocBlock类主要解决以下两个问题

  1. Block超出变量作用域可存在的理由
  2. __block变量在结构体成员变量__forwarding存在的理由

配置在全局变量上的Block,从变量作用域外也可以通过指针安全地使用,但设置在栈上的Block,如果气所属的变量作用域结束,该Block就废弃。由于__block变量也配置在栈上,所以它所属的变量作用域结束,则该__block变量也会被废弃。

Blocks提供了将Block和__block变量从栈上复制到堆上的方法来解决这个问题。将配置在栈上的Block复制到堆上,这样即使Block语法记述的变量作用域结束,堆上的Block还可以继续存在。

复制到堆上的Block将_NSConcreteMallocBlock类对象写入Block用结构体实例的成员变量isa。

impl.isa = &_NSConcreteMallocBlock;
复制代码

而__block变量用结构体成员变量__forwarding可以实现无论__block变量配置在栈上还是堆上时都能够正确地访问__block变量.

Blocks提供的复制方法:当ARC有效时,大多数情形下编译器会恰当地进行判断,自动生成将Block从栈上复制到堆上的代码。

typedef int (^blk_t)(int);

blk_t func(int rate){
    return ^(int count){
        return rate * count ;
    };
};

int main(int argc, char * argv[]) {
       
}
复制代码

转换时报错

/var/folders/6_/v3tcjsp9277_82bgpvhr8l6c0000gn/T/main-29bba4.mi:441:12: error: 
      returning block that lives on the local stack
复制代码

原因为该源代码返回配置在栈上的Block 函数,即程序执行中从该函数返回函数调用方时变量作用域结束,因此栈上的Block也被废弃。

但是该源代码通过对应的ARC的编译器可转换如下:

blk_t func(int rate)
{
	blk_t temp = &__func_block_impl_0(__func_block_func_0,&__func_block_desc_0_DATA,rate);
	temp = objc_retainBlock(temp);
	
	return objc_autoreleaseReturnValue(temp);
}
复制代码

通过objc4运行时库的runtime/objc-arr.mm可知,objc_retainBlock函数实际上就是_Block_copy函数,即:

/*
 * 将通过Block语法生成的Block,即配置在栈上的Block用结构体实例赋值给相当于Block类型的变量temp中。
 */
 
temp = _Block_copy(temp);

/*
 * _Block_copy函数将栈上的Block复制到堆上。
 * 复制后,将对上的地址作为指针复制给变量temp。
 */
	
return objc_autoreleaseReturnValue(temp);

/*
 * 将堆上的Block作为OC对象注册到autoreleasepool中,然后返回该对象。
 */ 
复制代码

大多数情况下编译器会适当地进行判断,将Block从栈上复制到堆上。那么不能进行判断的有以下几种情况:

  • 向方法或函数的参数中传递Block时

但是如果在方法或函数中适当地复制了传递过来的参数,那么就不必在调用该方法或函数前手动复制了。以下方法或函数不用手动复制:

  1. Cocoa框架的方法且方法名中含有usingBlock等时
  2. Grand Central Dispatch的API

举个例子,在使用NSArray类的enumrateObjectsUsingBlock实例方法以及dispatch_async函数时,不用手动复制。相反地,在NSArray类的initWithObjects实例方法上传递Block时需要手动复制。

- (void)viewDidLoad {
    [super viewDidLoad];

    id obj = [self getBlockArray];
    typedef void (^blk_t)(void);
    
    blk_t blk = (blk_t)[obj objectAtIndex:0];
    
    blk();
    
}


-(id) getBlockArray{
    int val = 10;
    return [NSArray arrayWithObjects:^{NSLog(@"blk0:%d",val);},^{NSLog(@"blk1:%d",val);}, nil];
}

复制代码

运行这段代码发现异常,这是由于getBlockArray执行结束时,栈上的Block被废弃了。修改getBlockArray方法如下即可,但是将Block从栈上复制到堆上是相当消耗CPU资源的。

-(id) getBlockArray{
    int val = 10;
    return [NSArray arrayWithObjects:[^{NSLog(@"blk0:%d",val);} copy],[^{NSLog(@"blk1:%d",val);} copy], nil];
    
}

复制代码
Block的类副本资源的配置存储域复制效果
_NSConcreteStackBlock从栈复制到堆
_NSConcreteGlobalBlock程序的数据区域什么也不做
_NSConcreteMallocBlock引用计数增加

由上表可知,不管Block配置在何处,用copy方法复制都不会引起任何问题,在不确定是调用copy方法即可。

但是在ARC中不能显示地release,调用多次copy完全没有问题,解释如下:

blk = [[[[blk copy] copy] copy] copy];
复制代码

解释如下:

{
	/** 将配置在栈上的Block赋值给变量blk中。*/
	
	blk_t temp = [blk copy];
	
	/** 将配置在堆上的Block复制给变量temp中,变量temp持用强引用的Block。*/
	
	blk = temp;
	
	/*
	 * 将变量temp的Block赋值给变量blk,变量blk持有强引用的Block。
	 * 因为原先赋值的Block配置在栈上,所以不受此复制的影响。
	 * 此时Block的持有者为变量blk和变量temp。
	 */
}

/*
 * 由于变量作用域结束,所以变量temp被废弃,其强引用失效并释放所持有的Block
 * 由于Block被变量blk持有,所以没有被释放。
 */

{
	/*
	 * 配置在堆上的Block被赋值变量blk,同时变量blk持有强制引用的Block
	 */ 
	 
	blk_t temp = [blk copy];
	
	/*
	 * 配置在堆上的Block被赋值到变量temp中,变量temp持有强引用的Block。
	 */
	 
	blk = temp;
	/*
	 * 由于向变量blk进行了赋值,所以第一次赋值的Block的强引用失效,Block被释放。
	 * 由于Block被变量temp所持有,所以没有被废弃。
	 * 变量blk中赋值了变量temp的Block,变量blk持有了Block的强引用。
	 * 此时Block的持有者为变量blk和变量temp。
	 */ 
}

/*
 * 由于变量作用域结束,所以变量temp被废弃,其强引用失效并释放所持有的Block
 * 由于Block被变量blk持有,所以没有被释放。
 */

/*
 * 下面重复此过程
 */
复制代码

__block 变量存储域

使用__block变量的Block从栈复制到堆上时,__block变量也会收到影响。

__block变量的配置存储域Block从栈复制到堆时的影响
从栈复制到堆并被Block持有
被Block持有

若在1个Block中使用__block变量,当该Block从栈复制到堆时,使用的所有__block变量也必定配置在栈上;这些__block也全部被从栈上复制到堆。此时,Block持有__block变量。即使在该Block已复制到堆的情况下,复制Block也对所使用的__block变量没有任何影响。

在多个Block中使用__block变量时,因为最先会将所有的Block配置在栈上,所以__block变量也会配置在栈上。在任何一个Block从栈复制到堆时,__block变量也会一并从栈复制到堆并被该Block所持有。当剩下的Block从栈复制到堆时,被复制的Block持有__block变量,并增加__block变量的引用计数。

如果配置在堆上的Block被废弃,那么它所使用的__block变量也就被释放。

使用__block变量的Block持有__block变量。如果Block被废弃,它所持有的__block变量也就被释放。这与OC的引用计数式内存管理的方式完全相同。

__block变量用结构体成员变量__forwarding,不管__block变量配置在栈上还是堆上,都能够正确地访问该变量。

__block int val =0;
    
void (^blk)(void)=[^{++val;} copy];
    
++val;
    
blk();
    
NSLog(@"%d",val);

复制代码

执行结果为:

2017-12-13 20:33:37.791190+0800 ImageOrientation[6953:2428079] 2

复制代码

在变换Block语法的函数中,该变量val为复制到堆上的__block变量用结构体实体,而使用的与Block无法的变量val,为复制前栈上的__block变量用结构体实例。

但是栈上的__block变量用结构体实例在__block变量从栈复制到堆上式,会将成员变量__forwarding的值替换为复制目标堆上的__block变量用结构体实例的地址。通过该功能,无论是在Block语法中、Block语法外使用__block变量,还是__block变量配置在栈上还是堆上,都可以顺利地访问同一个__block变量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值