Block转换为Block的结构体类型的自动变量,_block变量转换为_block变量的结构体类型的自动变量,所谓结构体类型的自动变量,即栈上生成的该结构体的实例。我们已经了解了block时oc对象,该block的类为_NSConcereteStackBlock,虽然该类并没有出现以变换源代码中,还有很多类似的类
_NSConcereteStackBlock 对象存储域在栈上
_NSConcereteGlobalBlock 域全局变量一样,设置在程序的数据区域(.data中)
_NSConcereteMallocBlock 设置在malloc函数分配的内存(堆上)块中。
下面我们介绍一下一个应用程序的内存分配
并非不是所有的block都是NSConcereteStackBlock,但是事实并不是这样的,在记述全局变量的地方使用Block语法时,生成的block为_NSConcereteGlobalBlock类对象。
#import <Foundation/Foundation.h> void(^blk)(void)=^{printf("Block");}; int main() { blk(); return 0; }
将上面源代码进行转换之后得到结果:
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是全局变量,因为在全局变量的地方不能够使用局部变量,由此可以得知此结构体实例不依赖任何执行时候的状态,所以整个程序只需要一个实例,即使在函数内而不是记述广域变量的地方使用block语法时,之遥block不截获自动变量,就可以将block用结构体设置在程序的数据区域。
在出现一下两种方式的时候,block是_NSConcereteGlobalBlock 类对象
1 记述全局变量的地方使用block语法
2 block语法的表达式中不使用应截获的自动变量的时候。
除此之外的block都要配置在栈上。那么现在问题就来了,我们什么时候使用_NSConcereteMallocBlock,总不能不用,这正是为了解决下面两个问题,
1 Block超出变量作用域但是却可存在。
2 _block变量用结构体成员变量_fowarding存在的原因。
我们需要了解,配置在全局变量上的block在变量作用域外也可以安全的访问,但是配置在栈上的,如果其所属的变量作用域结束,该block就被遗弃,所以Blocks提供了将栈上的block复制到堆上,这样即使block语法记述的变量作用域结束,堆上的block还可以继续存在。
_block变量存储域
需要注意的是,将栈上的_block变量用结构体实例在_block变量从栈上复制到堆上的时候,会将成员变量的_forwarding的值替换为复制目标的堆上的_block变量的结构体实例的地址。