Block

用一张图来解释:

blockObjective-c语言对闭包的实现:

闭包是一个函数(或指向函数的指针),再加上该函数执行的外部的上下文变量(有时候也称作自由变量)。

Block解释

#Block中变量的复制与修改.

block之外的变量引用,block默认是将其复制到其数据结构中来实现的。入下图:(注意是定义block的时候复制的)

Block中变量的复制与修改

通过block进行闭包的变量是const的。也就是说不能在block中直接修改这些变量。 如果修改,编译器会报错。如果确实需要在block中处理变量,用__block关键字声明变量.

对于用__block修饰的外部变量引用,block是复制其引用地址来实现访问的,如下图:

Block中变量的复制与修改

Block的类型

block有几种不同的类型:

  • NSConcreteGlobalBlock:全局静态Block,不访问任何外部变量,不会涉及到任何拷贝.
  • NSConcreteStackBlock:有访问到外部变量的Block,该保存在栈中,当函数返回时被销毁。
  • NSConcreteMallocBlock:保存在堆中的Block,引用计数为0的时候被销毁.该类型的Block是由NSConcreteStackBlock 复制到堆中形成的。

NSConcreteGlobalBlock类型的Block

例子:

void(^p)() = ^(){ 
    printf("Hello, World!\n"); 
 } 

NSConcreteGlobalBlock类型的Block要么是空的Block,要么是不访问任何外部变量的block它既不在栈中,也不在堆中,我理解为它可能在内存的全局区。

NSConcreteStackBlock 类型的Block

例子:

int a;
void(^p)() = ^(){ 
    printf("int a = %d\n",a); 
}      

_NSConcreteStackBlock类型的block有闭包行为,也就是有访问外部变量,并且该block只且只有有一次执行,因为栈中的空间是可重复使用的,所以当栈中的block执行一次之后就被清除出栈了,所以无法多次使用。

NSConcreteMallocBlock类型的Block

例子:

void exampleB_addBlockToArray(NSMutableArray *array) {
    char b = 'B';
    //将block 放入到数组中
    [array addObject:^{
            printf("%c\n", b);
    }];
}
void exampleB() {
    NSMutableArray *array = [NSMutableArray array];
    exampleB_addBlockToArray(array);
    //复制行为导致block 是从栈中复制到堆中。
    void (^block)() = [array objectAtIndex:0];
    block();
}

_NSConcreteMallocBlock类型的block有闭包行为,并且该block需要被多次执行。当需要多次执行时,就会把该block从栈中复制到堆中,供以多次执行。

#Block的数据结构定义

Block_layout就是对block结构体的定义:

Block的数据结构定义

对应结构体如下:

struct Block_descriptor { 
    unsigned long int reserved; 
    unsigned long int size; 
    void (*copy)(void *dst, void *src); 
    void (*dispose)(void *); 
}; 

struct Block_layout { 
    void *isa; 
    int flags; 
    int reserved; 
    void (*invoke)(void *, ...); 
    struct Block_descriptor *descriptor; 
    /* Imported variables. */ 
}; 
  • isa指针:指向表明该block类型的类。所有对象都有该指针,用于实现对象相关的功能。

  • flags:按bit位表示一些block的附加信息,比如判断block类型、判断block引用计数、判断block是否需要执行辅助函数等。

  • reserved:保留变量,我的理解是表示block内部的变量数。

  • invoke:函数指针,指向具体的block实现的函数调用地址。

  • descriptor: block的附加描述信息,比如保留变量数、block的大小、进行copy或dispose的辅助函数指针。

  • variables:因为block有闭包性,所以可以访问block外部的局部变量。这些variables就是复制到结构体中的外部局部变量或变量的地址。

#ARC下使用Block的坑总结

参考文章:

Objective-C中的Block

深度围观block:第一集&第二集

深度围观block:第三集

Block剧终:Objective-C中的闭包性和匿名函数

6: http://www.cocoachina.com/ios/20130711/65755]: http://www.cocoachina.com/ios/20150109/10891.html