【问题标题】:Use of __autoreleasing in code snippet example在代码片段示例中使用 __autoreleasing
【发布时间】:2012-02-29 15:41:45
【问题描述】:

以下代码摘自“The iOS 5 Developer's Cookbook”,用于说明如何将字符串写入文件。它使用 __autoreleasing 没有任何解释。为什么有必要?

NSError __autoreleasing error;
...
if (![myString writeToFile:path atomically:YES error:&error)
{
    NSLog(.... error.localizedFailureReason  ...);
    return;
}

为什么不直接在堆栈上声明错误而不使用 __autoreleasing?

----- 编辑-----

附加问题:为什么作者声明的是 NSError 而不是 NSError*?

【问题讨论】:

    标签: ios


    【解决方案1】:

    当变量通过引用传递并在 ARC 程序中分配时,您会错过这段代码中实际发生的情况。以函数 (BOOL)save 为例:(NSError * __autoreleasing *)error

    在非 ARC 编程中,保存函数如下所示:

    - (BOOL)save:(NSError * __autoreleasing *)myError {
      *myError = [[[NSError error] retain] autorelease]
    }
    

    在 ARC 编程中,保存函数如下所示:

    - (BOOL)save:(NSError * __autoreleasing *)myError {
      *myError = [[NSError alloc] init];
    }
    

    尽管 ARC 代码看起来像这样,但两个保存函数都会创建一个已保留并自动释放的错误对象。

    这是因为在 ARC 版本中,myError 的指针类型决定了错误对象的内存管理会发生什么。实际上,只要指针是 __autoreleasing 类型,*myError 赋值行就替换

    *myError = [[[NSError error] retain] autorelease]
    

    在运行时。

    因此,如果我们能够以某种方式将错误类型的指针(例如 __strong)传递给保存函数,则会导致保存函数做错事。

    由于编译器会通过创建一个临时变量来防止这种情况发生,因此代码可以以任何一种方式工作,但是从 ARC 编程的角度来看,传入 __autoreleasing 类型以外的指针是没有意义的。

    【讨论】:

    • 很好的答案,是唯一真正解释引擎盖下发生了什么的答案之一。不过,我还有一个问题。为什么需要将地址传递给save的变量声明为__autoreleasing? ARC 转换指南说,否则会弹出一个临时变量,但编译器不能对此聪明吗?还是这个问题有语义上的原因?
    • @Norswap 想象一下 save 函数是预编译库的一部分。当您从保存函数返回时,ARC 需要知道错误对象 (**myError) 是否已经在自动释放池中。而这种情况下看save函数的源码是查不出来的,因为它没有。这是关于保留 ARC 系统的输入。这样做时不需要相同的东西的唯一原因(例如)myDict=[NSDictionary dictionary])是因为有一个严格的约定,即以这种方式返回的对象总是自动释放的。
    • 我想上面的评论比上面的答案更能直接回答 OP 的问题。回答时我感兴趣的是,ARC 实际上在幕后做的事情与程序员在 ARC 之前明确做过的事情相同。它是智能编译器的一部分,而不是新运行时引擎的一部分。
    • 谢谢,这解释得很好。
    【解决方案2】:

    这是自动引用计数 (ARC) 系统的提示。

    error 对象将分配在 NSString 的代码中的某个位置,因此在您的代码中将其声明为 __autoreleasing 可以让 ARC 知道存储特性是什么。也就是说,当error被设置时,它将是一个自动释放的对象。

    【讨论】:

    • 大多数时候 NSError 被声明为 NSError* 错误。但在这种情况下,它的 NSError 错误。即在 wrieToFile 之前创建了一个 NSError:这会产生后果吗?
    • 我实际上已使用NSError *error 提交了对您原始问题的编辑。我认为这一定是书中的一个错字:Obj-C 对象总是用指针引用,所以NSError error 会给出一个编译时错误。
    • 恐怕这个答案是不正确的。它不是“提示”,而是语义声明 - hints 可以忽略,semantics 不能。存储的引用也需要是自动释放的对象。有关更多背景信息,请参阅this question
    【解决方案3】:

    来自https://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html 的 ARC 发行说明:

    __autoreleasing 用于表示通过引用 (id *) 传递并在返回时自动释放的参数。

    声明变量时会隐式声明 __strong,但由于它是通过引用传递的,因此编译器需要提示才能执行正确的操作。是否为堆栈变量不影响保留/释放跟踪。

    【讨论】:

      【解决方案4】:

      代码:

      __autoreleasing  NSString *str = xx;
      

      将被 ARC 编译为:

      NSString *str = xx.autorelease
      

      【讨论】:

      • 没有属性的点语法不是最佳实践。此外,它并没有真正回答为什么在 ARC 中使用 [xx autorelease]
      【解决方案5】:

      嗯,对通过引用传递的对象使用 __autoreleasing 限定符是 ARC 约定。解释来自clang网站:

      4.3.2。 __autoreleasing 对象的存储时长

      如果一个程序声明了一个 __autoreleasing 对象 非自动存储期限。

      基本原理:自动释放池与当前线程和作用域相关联 根据他们的性质。虽然可能有临时对象 实例变量填充了自动释放的对象,没有 ARC 可以在那里提供任何形式的安全保证的方式。

      如果将非空指针分配给 __autorelease 对象,而自动释放池在范围内,然后在离开自动释放池的范围后读取该对象。

      【讨论】:

        猜你喜欢
        • 2013-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-02
        • 1970-01-01
        • 1970-01-01
        • 2022-01-13
        相关资源
        最近更新 更多