block介绍(五)ARC下的内存管理附注

“Block非官方编程指南”中没有下一篇文章了

“Block非官方编程指南”中没有更新的文章了

一、自动调用Block_copy()

在ARC下,当我们试图在栈上创建一个block时,一个Block_copy调用会被立即执行。

为了验证这样的结论,让我们声明一个非常简单的block,这个block的内容仅仅是打印一句log,不引用任何外部变量或者所在栈域变量。

我们已经设置了_Block_copy这个符号断点,Block_copy方法只是一个宏函数,它真正调用的是_Block_copy

// Type correct macros

#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
#define Block_release(...) _Block_release((const void *)(__VA_ARGS__))

block_arc_mem1

block_arc_mem2

从前面的章节中我们知道,在栈上创建block会得到栈block或者全局区block(取决于是否引用了所在栈的域中的变量),而无论如何,ARC都会执行copy操作,唯一不同的是,对全局区block调用copy不会发生任何改变,可以参考前面给出的block源码地址(或者更简单的,在汇编区单步调试也可以发现执行的代码不同)。

二、作为Property或者函数返回值

我们知道,在non-ARC下将一个栈block作为函数返回值是不正确的做法,我们必须先copy再返回,同理对于将block作为property,我们要对其声明copy属性。而在ARC下,由于自动copy的存在,我们的代码发生了变化,或者说简化:

@property (nonatomic) VoidBlockType aBlock;
id returnsABlock() {
    void(^voidBlock)() = ^{
        NSLog(@"%s", __PRETTY_FUNCTION__);
    };
    return voidBlock;
}

在ARC下这样的写法十分安全,但是根据Apple的文档,作为最佳实践,我们还是要为它声明copy属性,这样会增加代码的可读性:

Screen Shot 2017-07-25 at 10.22.19 AM

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW12

三、Block指针作为identifier

或许有时候,我们需要一个identifier来识别某个block,或者对block进行比较,由于ARC为立即为我们调用Block_copy,并且通过前面的章节我们知道一个已经被copy的block被反复copy只会增加其reference count而不会改变其地址,所以block的指针可以被作为identifier:

void *blockId = (__bridge void *)(block);
if (anotherBlockId == blockId) {
    //
}

 注意使用__bridge来指明没有任何ownership传递,因为我们只是想取得这个对象的地址而已。

评论模块尚未加载