深入探究Blocks----------内存管理

一、Block的本质
    在底层Block是以C语言结构体实现的,而在OC中Block的本质即为OC对象。
    通过 " clang -rewrite-objc” 对OC代码进行重新

   ^{ printf ( "tempBlock" );};
     
     这个Block会被转换成如下结构:
    
struct __block_impl {
 
void *isa;
 
int Flags;
 
int Reserved;
 
void *FuncPtr;
};


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)};

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;
  }
};


      每个OC对象都有一个isa指针指向其类对象,类似的通过转换后代码可以看到“ impl.isa = &_NSConcreteStackBlock;”Block的isa指针指向了“ _NSConcreteStackBlock”类对象。即该Block为 _NSConcreteStackBlock类对象

    除了_NSConcreteStackBlock之外还有两个与之类似的类_NSConcreteGlobalBlock和_NSConcreteMallocBlock。不同的Block类对象指示了不同的存储域对应不同的OC类型,具体整理如下:

clang类 OC类 存储域
_NSConcreteGlobalBlock__NSGlobalBlock__静态数据区
_NSConcreteStackBlock__NSStackBlock__
_NSConcreteMallocBlock__NSMallocBlock__
     通过clang -rewrite-objc Block只定义了_NSConcreteStackBlock和_NSConcreteGlobalBlock两种类型。而在OC中确实是存在三种类型的Block为了便于理解我们加入了“_NSConcreteMallocBlock”,OC中的Block类型可以通过简单的“NSLog”来查看。

二、 _NSConcreteGlobalBlock
    通常情况下Block都是_NSConcreteStackBlock类对象,都设置在栈上。但实际上并非全是这样:

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

   即使在函数内使用Block语法,在Block未截获任何变量的情况下,Block的结构体实例的内容同样不依赖与执行时状态,所以整个程序同样只需一个实例,此时Block结构体实例同样设置在静态数据区。在这种情况下通过 clang -rewrite-objc 转换的源代码通常是_NSConcreteStackBlock类对象,但实际上却不同,通过NSlog得到的对象为“__NSGlobalBlock__”。总结如下:  
  • 在记述全局变量的地方使用Block语法时生成的Block为_NSConcreteGlobalBlock类对象。
  • Block语法的表达式中不使用应截获的变量时生成的Block为_NSConcreteGlobalBlock类对象。

三、 _NSConcreteMallocBlock
    
    配置在全局变量上的Block,从变量作用域外也可以通过指针安全的使用。但设置在栈上的Block,如果其所属的变量作用域结束。该Block就被废弃,从变量作用域外访问该Block会导致程序崩溃。Blocks提供了将Block从栈上复制到堆上的方法来解决这个问题。将配置在栈上的Block复制到堆上,这样即使Block语法记述的变量作用域结束,堆上的Block还可以继续存在。 当一个栈上的Block被复制到堆上时它的内存管理就和一个OC对象一样了(引用计数管理)。
    在非ARC环境下对Block简单的执行copy操作即可将Block从栈上复制到堆上,需要注意的是对copy后的Block执行release操作以防止内存泄漏。
    在非ARC环境下当方法返回一个Block时通常的做法是 :
    return [[stackBlock copy] autorelease];
         copy操作将Block复制到堆上, autorelease操作防止内存泄漏。

    在ARC环境下,大多数情况下编译器会进行恰当的判断,自动生成将Block从栈上复制到堆上的代码,总结如下:
  •     当Block被strong类型指针引用时
  •         当Block作为方法返回值返回时
  •         当Block属性被copy修饰符修饰时

对于不同类型Block的copy操作总结如下:
Block的类 存储域 复制效果
_NSConcreteGlobalBlock静态数据区什么也不做
_NSConcreteStackBlock从栈复制到堆
_NSConcreteMallocBlock增加引用计数
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值