如何在ARC下将所有权从@autoreleasepool中转移出去

4
我有以下代码。
- (NSString *)stringByEscapingXMLEntities;
{   
    NSString *result;
    @autoreleasepool {
        result = [self stringByReplacingOccurrencesOfString:@"&" withString:@"&"];
        result = [result stringByReplacingOccurrencesOfString:@"\"" withString:@"""];
        // ... lot of -stringByReplacingOccurrencesOfString: calls
        result = [result stringByReplacingOccurrencesOfString:@" " withString:@" "];
    }
    return result;
}

现在我想知道如何将result的所有权从该方法中转移出去。在ARC之前,我会在退出自动释放池之前保留result并在方法末尾返回自动释放。

谢谢!


到目前为止,我尝试了以下方法:在自动释放池中返回;将结果定义为__strong,并使方法返回一个已保留的对象; - stigi
你在 ARC 下还使用自动释放池吗? - Eimantas
1
你那里的代码不起作用吗?我本以为编译器会知道 result 是在池子的范围之外声明的,会添加必要的保留。 - JeremyP
以上示例不起作用。在Mac OS上进行测试会崩溃。同时假设自动释放池是必要的。基本上,我只是想完全理解为什么它不起作用。 - stigi
1
那段代码对我来说是有效的。我们能看一下那个导致崩溃的代码吗?也许有更好的解决方案来解决真正的问题。 - Peter Hosey
4个回答

4
有两种方法可以实现这个目的:
  • 将方法重命名为像 copyStringByEscapingXMLEntities 这样的名称--copy 表示所有权的转移,ARC会相应地创建代码。
  • 在头部附加 NS_RETURNS_RETAINED 到方法定义中,像这样:- (NSString *)stringByEscapingXMLEntities NS_RETURNS_RETAINED

  • 编辑: 正如 'iljawascoding'所提到的,@autoreleasepool 没有真正需要保留 - 除了优化。
    编辑2: 记住:ARC总是做正确的事情。你尝试的所有事情(你的注释)都会导致完全相同的正确程序--尽管如果结果被定义为__strong,可能缺少一些优化。

    但是有没有一种方法可以在不改变方法签名的情况下完成这个操作? - stigi
    第二种方法是在头文件中添加 NS_RETURNS_NOT_RETAINED。该方法的名称和签名保持不变 -- 它只是一个属性。问题在于编译器需要知道类及每个调用的行为 -- 而唯一的方式就是公共接口。没有其他的方法。 - Max Seelemann
    抱歉,刚才看错了,我是指 NS_RETURNS_RETAINED 而不是 NS_RETURNS_NOT_RETAINED。 ;) - Max Seelemann
    这个答案是无意义的。使方法返回一个保留值不会影响ARC(正确地)处理从自动释放池范围中传输的方式。 - Jens Ayton
    @Ahruman:问题明确说明了:“我现在想知道如何将所有权结果从方法中传递出去。” 因此有了答案。 - Max Seelemann
    如果您继续阅读下一句话,它会澄清OP想要使用未修改代码的语义ARC。 - Jens Ayton

    2
    你所发布的代码是正确的。任何崩溃都有不同的原因。
    因为result是一个autorelease范围之外的强引用,ARC负责在退出池时保持其存活状态,并且它确实做到了。你不需要做任何特殊的处理。
    更具体地说,ARC会生成等效于以下代码的代码:
    void *_arp = objc_autoreleasePoolPush();
    temp1 = objc_retainAutoreleasedReturnValue([self stringByReplacingOccurrencesOfString:@"&" withString:@"&"]);
    temp2 = objc_retainAutoreleasedReturnValue([temp1 stringByReplacingOccurrencesOfString:@"\"" withString:@"""]);
    objc_release(temp1);
    result = objc_retainAutoreleasedReturnValue([temp2 stringByReplacingOccurrencesOfString:@" " withString:@" "]);
    objc_release(temp2);
    // result is not released here
    objc_autoreleasePoolPop(_arp);
    return objc_autoreleaseReturnValue(result); // Result is returned autoreleased, or handed off to a  matching objc_retainAutoreleasedReturnValue() in the caller.
    

    请注意,临时变量使用objc_retainAutoreleasedReturnValue/objc_release对进行处理。由于objc_retainAutoreleasedReturnValueobjc_retainAutoreleasedReturnValue实现的运行时优化,这意味着如果使用ARC构建-stringByReplacingOccurrencesOfString:,则临时值将立即释放,而无需放入autorelease池中。我的猜测是,大多数系统框架尚未使用ARC。请保留HTML标记,以便正确显示格式。

    2
    摒弃自动释放池。根据苹果公司的说法,ARC会在你的代码完成对'result'中临时实例的使用后自动插入必要的释放操作。在这里,自己编写自动释放池没有任何好处。请保留HTML标签。

    2
    如果方法被频繁调用并且想要确保中间对象被释放,那么自动释放池可能是有意义的。 - Max Seelemann

    0
    为什么不在@autoreleasepool作用域关闭之前使用[[NSString alloc] initWithString:result]呢?但是,首先为什么要使用ARP呢?

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接