block能够捕获外界变量的原因:在定义block的时候,外界变量被编译器转换成了结构体成员变量,并且在调用block的时候,这些变量的值会被拷贝到block的结构体实例中。这样一来,即使在block执行之后,这些变量的作用域已经结束,它们的值也能够保留下来,并且能够在block内部继续使用。
一、block能够捕获外界变量的原因
在定义Block的时候,外界变量被编译器转换成了结构体成员变量,并且在调用Block的时候,这些变量的值会被拷贝到Block的结构体实例中。这样一来,即使在Block执行之后,这些变量的作用域已经结束,它们的值也能够保留下来,并且能够在Block内部继续使用。这种特性就是Block所具有的闭包特性,也是它能够捕获外界变量的原因。
二、block是什么
block是一种封装了代码块的数据类型,可以在C、Objective-C和Swift中使用。它类似于函数或方法,但具有更灵活的特性,可以嵌套在其他代码块中使用,并且能够捕获外部变量。block可以作为参数传递给函数或方法,也可以作为返回值返回。在异步编程、多线程和事件处理等场景中,block被广泛应用。
block就是一个代码块, block是将函数及其执行上下文封装起来的对象,是一个匿名的函数对象, block也有isa。既然block内部封装了函数,那么它同样也有参数和返回值,本身也可以被作为参数在方法和函数间传递。
block标准语法:
return_type (^blockName)(var_type) = ^return_type (var_type varName) {
// ...
};
blockName(var);
三、Block底层实现
block的底层实现是结构体,和类的底层实现类似,都有isa指针,可以把block当成是一个对象。下面通过创建一个控制台程序,来窥探block的底层实现。
block 的内存结构图:
Block_layout结构体成员含义如下:
- isa: 指向所属类的指针,也就是block的类型
- flags: 标志变量,在实现block的内部操作时会用到
- Reserved: 保留变量
- invoke: block执行时调用的函数指针,block内部的执行代码都在这个函数中
- descriptor: block的详细描述,包含 copy/dispose 函数,处理block引用外部变量时使用
- variables: block范围外的变量,如果block没有调用任何外部变量,该变量就不存在
Block_descriptor结构体成员含义如下:
- reserved: 保留变量
- size: block的内存大小
- copy: 拷贝block中被 __block 修饰的外部变量
- dispose: 和 copy 方法配置应用,用来释放资源
具体实现代码:
enum {
BLOCK_REFCOUNT_MASK = (0xffff),
BLOCK_NEEDS_FREE = (1 << 24),
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), /* Helpers have C++ code. */
BLOCK_IS_GC = (1 << 27),
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_DESCRIPTOR = (1 << 29)
};
/* Revised new layout. */
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. */
};
延伸阅读1:block类型
- NSGlobalBlock:没有访问auto变量
- NSStackBlock:访问了auto变量
- NSMallocBlock:调用了copy