【问题标题】:What is Apple warning against in ARC documentation caveat about pass by reference?Apple 在 ARC 文档中警告关于通过引用传递的警告是什么?
【发布时间】:2014-03-31 23:37:06
【问题描述】:

在 Apple 关于 ARC 的文档中,他们特别指出了一个有问题的场景,即 ARC 将在幕后生成样板临时变量。搜索“编译器因此重写”:

https://developer.apple.com/library/mac/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html

警告的要点似乎是因为基于堆栈的变量是“强”的,并且被调用方法 (performOperationWithError:) 的引用参数是自动释放的,ARC 将生成一个临时局部变量来服务内存自动释放变量的管理需求。但是因为在样板示例中临时变量被分配给强变量,所以从客户的角度来看似乎没有风险。

文档在这里煞费苦心地警告我们的到底是什么?作为客户端或作为可能以这种方式调用的方法的实现者(使用自动释放的按值返回参数)有什么风险?

【问题讨论】:

    标签: ios objective-c macos cocoa automatic-ref-counting


    【解决方案1】:

    这只是对性能不理想的警告。在改写的代码中,“tmp”指向的NSError自动释放回来,分配给“error”时被保留,当“error”超出范围时再次释放。

    如果将原始代码中的声明更改为: NSError __autoreleasing *error;

    如果您这样做,则不会分配给临时对象,并且不会再发生隐式保留然后释放。 (NSError 对象本身仍然像以前一样有效,因为它仍在自动释放池中。)因此文档警告您,如果您使用“错误”变量限定符,它可能会导致额外的保留计数其他情况下不需要的处理。

    另请注意,对于任一版本的代码:因为有问题的变量是通过引用传递的,而不是来自 -performOperationWithError: 的返回值,所以没有机会执行 ARC 可以执行的魔术堆栈遍历技巧这样做是为了避免对象首先进入自动释放池。

    【讨论】:

    • 谢谢!我担心的主要原因是文档中的措辞“您还需要注意通过引用传递的对象”。如果他们说出原因,那会很有帮助。看来您的解释可能是您关心的主要考虑因素。
    【解决方案2】:

    如果您开始查看传递给方法的值,我认为这是为了防止混淆。在他们的示例中,如果我在调用 [myObject performOperationWithError:&tmp]; 的行上设置断点并键入 p error,我将看到它的地址。但如果我进入-performOperationWithError: 并输入p error,我会得到一个不同的 值——在方法内部,error 指向那个临时值。

    我可以看到一些糟糕的 sap 尝试使用 ARC 调试一些棘手的问题,其中指针在传递到方法中时发生变化将是一个非常令人困惑的红鲱鱼。

    【讨论】:

      【解决方案3】:

      我的猜测:如果您对输出参数引用的内存做出假设,例如索引指针,您可能会感到惊讶。

      【讨论】:

      • 那也是!一般来说,为 Objective-C 对象混合 ARC 和指针算术应该非常小心,并且只能作为最后的手段。
      • 是的,有趣的东西。我认为他们应该将其更多地表述为信息性而不是介绍它“您还需要注意对象”
      【解决方案4】:

      我认为这与客户无关。它看起来像是对 WWDC 2013 内存问题视频中解决的相同问题的引用:如果您自己实现一个采用自动释放间接参数(例如 NSError**)的方法,并且如果您在该方法内创建一个自动释放池块,不要从自动释放池块内分配给 NSError。而是赋值给一个局部变量,然后从局部赋值给autorelease pool块外的NSError。

      【讨论】:

      • 但这只是猜测。无论如何,请观看视频并亲自查看您是否认为它相关。如果你在这里没有得到满意的答案,你可能会做得很好,在可可开发列表上提问,格雷格帕克本人很可能会从马口中弹出一个直接的答案。这就是我总是能获得关于这类事情的最佳信息的地方。
      • 谢谢!这是一个有趣的细微差别,我希望他们在文档中更公开地提到它。
      【解决方案5】:

      在我看来,与其说是对这种行为的警告,不如说是对编译器在这种情况下所做的事情的描述以及为什么可以将强本地错误引用的地址传递给声明为需要 __autoreleasing 引用的方法并且不会引发投诉。

      您通常希望 API 对此类参数使用 __autoreleasing,以防它被 ARC 或非 ARC 代码使用,因为在非 ARC 代码中,必须释放此类输出参数是不常见的。

      【讨论】:

      • 谢谢,特洛伊!听起来我没什么好担心的。感谢您的回复。
      【解决方案6】:

      Apple 文档中提到了一个编译器misfeature,它会为你合成一个临时变量来处理 __block 和 __autoreleasing 之间的转换。遗憾的是,这并不能解决很多问题并且它会产生潜在的灾难性意外结果。

      例如:

      int main(int argc, char *argv[])
      {
          __block id value = @"initial value";
          void (^block)(id *outValue) = ^(id *outValue){
              value = @"hello";
          };
          block(&value);
          NSLog(@"value = %@", value);
          return 0;
      }
      

      使用 ARC,此报告:

      2013-04-24 13:55:35.814 block-local-address[28013:707] value = initial value
      

      但有 MRR:

      2013-04-24 13:57:26.058 block-local-address[28046:707] value = hello
      

      在使用 NSFileCoordinator 时经常会出现这种情况,导致您丢失生成的 NSError!

      #import <Foundation/Foundation.h>
      
      int main(int argc, char *argv[])
      {
          NSURL *fileURL = [NSURL fileURLWithPath:@"/tmp/foo"];
          NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
      
          __block NSError *error;
          [coordinator coordinateWritingItemAtURL:fileURL options:0 error:&error byAccessor:^(NSURL *newURL){
              NSDictionary *userInfo = @{
                  NSLocalizedDescriptionKey : @"Testing bubbling an error out from a file coordination block."
              };
              error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOSYS userInfo:userInfo];
          }];
      
          NSLog(@"error = %@", error);
      }
      

      使用 ARC 编译时,这会导致 nil 错误!

      这已经写成a bug at llvm.org 有一段时间了,尽管我只是更改了标题以更清楚地表明我建议删除该功能。该错误还附加了一个补丁,用于添加新标志-fno-objc-arc-writeback 以禁用该功能。

      【讨论】:

      • 我还应该注意,如果将这些变量标记为__autoreleasing,编译器将正确警告将其同时用作__autoreleasing_block
      • 哇!谢谢,蒂姆。我很高兴知道文档的模糊性可能反映了行为正当性的模糊性!
      猜你喜欢
      • 1970-01-01
      • 2012-03-02
      • 1970-01-01
      • 1970-01-01
      • 2011-02-06
      • 2011-07-07
      • 2021-11-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多