对Block的内存使用相关的内容简要整理,解释其中的道理和使用Block需要注意的问题。
1. Block与对象
首先我们先反思几个问题:
block到底是不是对象?
如果是对象,和某个已定义的类的实例对象在使用上是不是一样的?
如果不一样,主要的区别是什么?
对于第一个问题,苹果的Objective-C官方文档中在“Working with Blocks”明确说明:
“Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary.”
可见,Block是Objective-C语言中的对象。
苹果在block的文档中也提过这么一句:
“As an optimization, block storage starts out on the stack—just like blocks themselves do.”
Clang的文档中也有说明:
“The initial allocation is done on the stack,but the runtime provides aBlock_copyfunction” (Block_copy在下面我会说)
凭这一点,我们就可以回答剩下的两个问题。Block对象与一般的类实例对象有所不同,一个主要的区别就是分配的位置不同,block默认在栈上分配,一般类的实例对象在堆上分配。而这正是导致本文最初提到的那个问题发生的根本原因。Block对象在栈上分配,block的引用指向栈帧内存,而当方法调用过后,指针指向的内存上写的是什么数据就不确定了。但是到此,retain的疑问还是没有解开。
我们想一想Objective-C引用计数的原理,retain是对一个在堆中分配内存的对象的引用计数做了增加,执行release操作的时候检查计数是否为1,如果是则释放堆中内存。而对于在栈上分配的block对象,这一点显然有所不同,如果方法调用返回,栈帧上的数据自然会作废处理,不像堆上内存,需要单独release,就算NSArray对block对象本身做了retain也无济于事。
Clang文档中提到:
“Block pointers may be converted to typeid; block objects are laid out in a way that makes them compatible with Objective-C objects. There is a builtin class that all block objects are considered to be objects of; this class implementsretainby adjusting the reference count, not by callingBlock_copy.”
那么要是用一个方法对block数组做初始化是否有可行方案呢。答案是肯定的,不过需要真正了解block的使用,至少要会用Block_copy()和Block_release()。
2. Block的类型和使用
在Clang的文档中,定义了两个Block类型: _NSConcreteGlobalBlock
和 _NSConcreteStackBlock。
因此,也就得到了下面对block的使用注意点。
对于Global的Block,我们无需多处理,不需retain和copy,因为即使你这样做了,似乎也不会有什么两样。对于Stack的Block,如果不做任何操作,就会向上面所说,随栈帧自生自灭。而如果想让它获得比stackframe更久,那就调用Block_copy(),让它搬家到堆内存上。而对于已经在堆上的block,也不要指望通过copy进行“真正的copy”,因为其引用到的变量仍然会是同一份,在这个意义上看,这里的copy和retain的作用已经非常类似。
“The runtime provides aBlock_copyfunction which, given a block pointer, either copies the underlying block object to the heap, setting its reference count to 1 and returning the new block pointer, or (if the block object is already on the heap) increases its reference count by 1. The paired function isBlock_release, which decreases the reference count by 1 and destroys the object if the count reaches zero and is on the heap.”
在类中,如果有block对象作为property,可以声明为copy。
3. 其它
在苹果官方的《Transitioning to ARC Release Notes》文档中,写了这样一段话,大家理解一下,尤其是其中的“just work”。
“How do blocks work in ARC?
Blocks ‘just work’ when you pass blocks up the stack in ARC mode,
such as in a return. You don’t have to call Block Copy any more.”
4. 参考
以上整理了对Block的理解,在开发中注意到这些点足以解决block的特殊性带来的各类问题。要想继续深入,可参看LLVM文档中对block的介绍:
http://clang.llvm.org/docs/Block-ABI-Apple.html
http://clang.llvm.org/docs/AutomaticReferenceCounting.html?highlight=class
网友评论