方法返回的对象会被放入自动释放池吗?

Will an object returned by a method be put into autorelease pool?

启用 ARC 后,o 是否会在此代码段中放入自动释放池?

- (NSObject *)obj {
    NSObject *o = [[NSObject alloc] init];

    return o;
}

另外,这两个代码片段有什么区别?

- (NSObject *)obj {
    NSObject * __autoreleasing o = [[NSObject alloc] init];

    return o;
}

对比

- (NSObject *)obj {
    NSObject * __strong o = [[NSObject alloc] init];

    return o;
}

When ARC is enabled, will o be put into autorelease pool in this code snippet?

答案是,可能会,也可能不会。 ARC 不以任何一种方式做出保证。这里方法的名称 (obj) 不以指示保留的 return 类型的特殊名称之一开头(例如 allocretainnewcopymutableCopy),所以它 return 是 non-retained 参考。 ARC specification section on "Unretained return values" 表示:

In the worst case, this may involve an autorelease, but callers must not assume that the value is actually in the autorelease pool.

在过去,在 MRC 下,在这种情况下,您必须在 return 之前执行 autorelease,因为该方法需要 return a non-retained 引用,但必须有人持有对 object 的强引用,否则它将被释放。由于 object 是在您的方法中创建的,因此没有其他人引用它,并且该方法必须在其范围末尾摆脱其强引用,因为它不能 return 保留引用,所以唯一的方法是让自动释放池在 return.

中保持强引用

ARC 必须保留 ABI-compatible 和 MRC(即您应该能够用 ARC 替换 MRC 实现,反之亦然,而无需更改 headers 并且调用者不需要知道它是否使用 ARC 或 MRC 编译才能正常工作)。因此,在从 MRC 代码调用您的方法的情况下,遵循与上一段相同的逻辑,您的方法必须 autorelease;没有其他方法可以做到这一点。

然而,ARC 引入了一个巧妙的可选运行时优化,可以在某些情况下消除 autorelease 当调用者和被调用者都使用 ARC 编译时,调用者或被调用者不需要了解另一方。它大致是这样工作的:在函数的 return 中,您通常会 autorelease,ARC 却调用 objc_autoreleaseReturnValue()。在调用函数并保留 return 值的代码中,它调用了 objc_retainAutoreleasedReturnValue()。在 objc_autoreleaseReturnValue() 中,它查看堆栈帧中的 return 地址以查看 returned 值是否将传递给 objc_retainAutoreleasedReturnValue()。如果没有,它将 autorelease。如果是,那么它将跳过 autorelease,并修改 return 地址以跳过 objc_retainAutoreleasedReturnValue,从而同时删除 autoreleaseretain ,抵消了。在objc_retainAutoreleasedReturnValue()中,如果不跳过,就直接retain。这适用于 MRC-to-ARC 调用、ARC-to-ARC 调用和 ARC-to-MRC 调用,并且会消除某些 ARC-to-ARC 调用的 autorelease


What's more, what is the difference between these two code snippets?

第一个会将其放入自动释放池,而第二个可能不会。 (顺便说一下,__strong 是默认值,所以第三段代码与第一段代码相同。)

但基本上没有充分的理由将 __autoreleasing 用于局部变量而不是默认值(即 __strong); __strong只是更简单,更容易思考。

通常你唯一会遇到 __autoreleasing 的情况是在 "pointer-to" 类型的参数中——如果一个函数声明它需要一个指向 __autoreleasing 的指针(例如 NSObject * __autoreleasing * ),这意味着它将以不同于使用指向 __strong 的指针(例如 NSObject * __strong *)的方式写入 pointed-to 变量。两种方式都有效,但调用者和被调用者需要就哪一种方式达成一致。由于 Cocoa 中 NSError * __autoreleasing * 的普遍使用,不合格的指针默认指向 __autoreleasing(例如 NSObject ** 表示 NSObject * __autoreleasing *)。