Block存储域
在 Objective-C 语言中,一共有 3 种类型的 block
Block的类
类 | 设置对象的存储域 |
---|---|
_NSConcreteStackBlock | 栈 |
_NSConcreteGlobalBlock | 程序的数据区域 (.data区) |
_NSConcreteMallocBlock | 堆 |
三种block在内存中的对应位置如图所示
前两章所说到的block都是属于_NSConcreteStackBlock类型的,并且设置在栈上的,并非全是如此。
block类型的区分
1、_NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
以下情况,block为_NSConcreteGlobalBlock类型
- block内部只使用了全局变量
- block内部没有使用任何外部的局部变量
除了以上两种情况,其他的block为_NSConcreteStackBlock类型。
而对于_NSConcreteMallocBlock,只有当_NSConcreteStackBlock类型的block执行copy操作(手动或者系统执行)时,该block才会是_NSConcreteMallocBlock类型
1、_NSConcreteGlobalBlock
void (^blk)(void) = ^(printf("Global Block \n"););
int main (){
}
上面的代码通过调用全局变量blk来使用block语法,如果转化成源代码,从block结构体的成员变量isa就可以看出 ,isa指向_NSConcreteGlobalBlock
struct __globalBlock_block_impl_0 {
struct __block_impl impl;
struct __globalBlock_block_desc_0* Desc;
__globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
Block的类为_NSConcreteGlobalBlock类,即该block用结构体实例设置在程序的数据区域中。因为使用全局变量的地方不能使用局部变量,所以不存在对变量的截取,在整个使用过程中,结构实体的内容不依赖执行时状态,Block用结构体每次截获的值都完全相同,因为只有一个实例,所以Block用结构体实例设置在与全局变量相同的区域就可以了。'
2、_NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。sa 指向 _NSConcreteStackBlock,说明这是一个分配在栈上的实例。
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
我们可以看出,
- isa 指向 _NSConcreteStackBlock,说明这是一个分配在栈上的实例。
- __main_block_impl_0 中增加了一个变量 age,在 block 中引用的变量 age实际是在申明 block 时,被复制到 __main_block_impl_0 结构体中的那个变量 a。因为这样,我们就能理解,在 block 内部修改变量 a 的内容,不会影响外部的实际变量 age。
- __main_block_impl_0 中由于增加了一个变量 age,所以结构体的大小变大了,该结构体大小被写在了 __main_block_impl_0 中。
3、_NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。
NSConcreteMallocBlock 类型的 block 通常不会在源码中直接出现,因为默认它是当一个 block 被 copy 的时候,才会将这个 block 复制到堆中。