autorelease的使用场景

1. 概述

autorelease是解决函数返回值引用计数问题的一种方式。

考虑这样一个场景,我们有个方法返回一个对象,在方法内部我们retain了这个对象,这意味这我们需要release这个对象,维持对象引用计数的平衡。但是因为这个对象是返回值,我们要确保调用方拿到的对象是没被回收的,在函数返回前release存在导致对象被回收的风险。

也就是说,我们需要release返回值,但又不能在函数返回前。这时候,我们有两个选择。要么延后release,要么调用方帮我们release。这分别对应着解决函数返回值引用计数问题的两种方式。

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

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


2. autorelease在实现上的优化

2.1 优化的场景

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

2.2 优化的思路

在上述场景下,省略autorelease和调用方的retain不会产生任何问题,因为:
1.返回值对象对象安全的穿透了作用域边界(因为没有进行release)。
2.返回值对象引用计数是平衡的。

2.3 优化的实现

编译器进行代码分析,判断函数返回值是否被立即retain,并设置标志。函数返回前的autorelease以及retain一个函数返回值被替换为objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue两个函数调用,在这两个函数中根据之前设置的标志来决定是什么都不做,还是真的autorelease和retain。


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

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


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

有两种情况,

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

问题在于,函数对调用方来说是黑盒,所以调用方无法确定返是否需要release一次函数返回值。

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

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


5. 并不是所有的函数返回值都需要autorelease

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

- (SomeClass *)someInstance
{
    return _someInstance;
}

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

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

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

猜你喜欢

转载自blog.csdn.net/fly1183989782/article/details/71325701