autorelease的使用场景

一、 概述

     autorelease 就是把 release 延后到 autorelease pool drain 的时候。是 ARC 进行引用计数管理的一个机制,挺巧妙的。使用场景是,我 retain 了一个对象,我有职责 release 它,但是现在不能的情况。因为我知道后续代码可能想要 retain 它 。

    考虑这样一个场景,我们有个方法返回一个对象,在方法内部我们 retain 了这个对象,这意味这我们需要 release 这个对象,维持对象引用计数的平衡。但是因为这个对象是返回值,我们要确保调用方拿到的对象是没被回收的,在函数返回前 release 的话,我们就不能确保这一点。也就是说,我们需要 release 返回值,但又不能在函数返回前。这时候,我们有两个选择。要么延后 release ,要么调用方帮我们 release。这分别对应着解决函数返回值引用计数问题的两种方式。

    第一种延后 release 就是 autorelease ,函数返回前不进行 release ,先把返回值暂存在 autorelease pool 中一段时间。这段时间内,调用方如果需要,可以 retain 这个返回值,等到 autorelease pool 干的时候,再去 release 这个对象,平衡对象引用计数,适用于除 alloc 、copy 、new 、mutableCopy 之外的函数返回值。

    第二种是函数调用方负责 release 函数返回值,函数和函数调用方配合维护引用计数,适用于 alloc、copy、new、mutableCopy 之类的函数。


二、 autorelease在实现上的优化

1. 优化的场景

    假设我们有个方法,返回一个对象,方法内部我们 retain 了这个对象,返回的时候我们 autorelease 了这个对象。调用方拿到我们的返回值对象,立即进行了 retain,使用完之后进行 release。

2. 优化的思路

在上述场景下,省略autorelease和调用方的retain不会产生任何问题,因为:

  1. 返回值对象对象安全的穿透了作用域边界(因为没有进行 release)。
  2. 返回值对象引用计数是平衡的。
3. 优化的实现

    编译器进行代码分析,判断函数返回值是否被立即 retain,并设置标志。函数返回前的 autorelease 以及 retain 一个函数返回值被替换为objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue两个函数调用,在这两个函数中根据之前设置的标志来决定是什么都不做,还是真的 autorelease 和 retain 。(果然,软件工程当中,没有什么问题是增加中间层解决不了的,这里的 autoRelease 、retain 都会替代为中间层 objc_autoreleaseReturnValue、objc_retainAutoreleasedReturnValue,这两个方法会根据标志执行动态化逻辑)


三、 为什么alloc、copy、new、mutableCopy之类的方法返回值不autorelease,而是把release的责任交给调用方?

    为了运行效率、内存使用效率。 把对象加入 autorelease pool 是有性能开销的,如果 alloc 方法的返回值也 autorelease 的话,在没有编译器和运行时的优化的情况下,所有的对象都要等到 autorelease pool 干的时候才有可能被回收。而函数返回前不 release,将 release 的责任转移给调用方,调用方可以在使用完返回值之后及时的 release 返回值,从而提升内存使用效率。


四、为什么不所有方法都像 alloc、copy、new、mutableCopy 之类的方法一样,把 release 的责任交给调用方,而使用 autorelease ?

    有两种情况,

  • 函数内部 retain 了返回值,需要调用方进行一次 release 来balance。
  • 函数内部没有 retain 返回值,不需要调用方进行一次 release 来balance。(比如我返回的类的成员变量)

    问题在于,函数对调用方来说是黑盒,所以调用方无法确定返是否需要release一次函数返回值。而 alloc、copy、new、mutableCopy内部一定有一次retain,(如果你在这些函数中返回一个你没有 retain 的对象,系统会自动加上 retain 语句), 所以确定需要进行一次 release ,因而可以把 release 的责任交给调用方。

    总的来说,要把release责任交给调用方,函数要具有确定性。


五、并不是所有的函数返回值都需要autorelease

    autorelease 是特殊的 release,作用也是让引用计数减一。如果函数体内没有 retain,就不需要 autorelease。举个例子,类property的get方法返回值是不需要 autorelease的,直接返回即可。

- (SomeClass *)someInstance
{
    return _someInstance;
}

    当然,需要不需要是一回事,可以不可以是另外一回事,你可以去做不需要的事,比如把 get 方法实现成下面这样。

- (SomeClass *)someInstance
{
    return [[someInstance retain] autorelease];
}

    这样的结果是对象的属性的生命周期可能长于对象本身。我觉得不符合“属性归属于对象,生命周期依赖于对象的认知”。不支持这么实现get方法。

你可能感兴趣的:(iOS)