【问题标题】:Defining NSMutableString?定义 NSMutableString?
【发布时间】:2009-09-28 15:33:16
【问题描述】:

我的理解是这两个都创建了一个 NSMutableString,只有第一个是系统拥有的,第二个是我拥有的(即我需要释放它)。有什么特别的原因为什么我应该使用其中一个,从表面上看,使用第一个似乎更容易?还有第一个更好,因为它给了编译器一种大小感?

NSMutableString *newPath = [NSMutableString stringWithCapacity:42];

NSMutableString *newPath = [[NSMutableString alloc] init];

编辑...也

我看到很多声明写在两行(即)

NSMutableString *newPath;
newPath = [NSMutableString stringWithCapacity:42];

我个人更喜欢单线,这只是个人风格的另一个例子吗?

【问题讨论】:

    标签: objective-c cocoa nsmutablestring


    【解决方案1】:
    NSMutableString *newPath = [NSMutableString stringWithCapacity:42];
    

    NSMutableString *newPath = [[NSMutableString alloc] init];
    

    有什么特别的原因为什么我应该使用其中一个,从表面上看,使用第一个似乎更容易?

    是的。除非您有特定的理由不这样做,否则请始终立即自动释放。

    第一个原因是很容易忘记写release 消息。如果您在创建对象的同一语句中自动释放该对象(如在[[[… alloc] init] autorelease] 中),那么忘记它会更加困难,而且当您这样做时会更加明显。便利的工厂方法(如stringWithCapacity:)为你自动释放对象,所以就像你自己自动释放它一样,你不必担心以后释放它。

    其次,即使您确实记得写单独的release 消息,也很容易不点击它。两种方式是提前回报:

    NSString *str = [[NSString alloc] initWithString:@"foo"];
    
    BOOL success = [str writeToFile:path atomically:NO];
    if (!success)
        return;
    
    [str release];
    

    并抛出或传播异常:

    NSString *str = [[NSString alloc] initWithString:@"foo"];
    
    //Throws NSRangeException if str is not in the array or is only in the array as the last object
    NSString *otherStr = [myArray objectAtIndex:[myArray indexOfObject:str] + 1];
    
    [str release];
    

    “不这样做的具体原因”通常是您有一个创建大量对象的紧密循环,在这种情况下,您可能需要手动管理尽可能多的循环中的对象,以保持你的对象倒计时。但是,只有在您有证据表明这是您的问题时才这样做(无论是来自 Shark 的硬数字、来自 Instruments 的硬数字,还是当循环运行时间足够长时您的系统就会进入分页地狱)。

    其他可能更好的解决方案包括将循环拆分为两个嵌套循环(外部循环用于为内部循环创建和排出自动释放池)并切换到 NSOperation。 (但是,请确保您对队列一次运行的操作数设置了限制——否则,you may make it even easier to go into paging hell。)

    第一个更好,因为它让编译器有大小感?

    更好,但不是因为这个原因。

    对于编译器来说,这只是另一个类消息。编译器不知道也不关心它做了什么;就其所知和所关心的,stringWithCapacity: 是向用户播放歌曲的消息。

    它确实为 NSMutableString 提供了大小提示——该类将知道它最初可能想要分配多少字符存储空间。您从中获得的任何好处都可能很小(至少在 Mac 上),但是如果您手边有这些信息,为什么不使用它呢?相反,我不会特意去计算它。

    我看到很多声明写在两行(即)

    NSMutableString *newPath;
    newPath = [NSMutableString stringWithCapacity:42];
    

    我个人更喜欢单线,这只是个人风格的另一个例子吗?

    是的。但是,使变量未初始化存在一定的风险。如果您决定养成这样的习惯,请务必打开“运行静态分析器”构建设置。

    【讨论】:

    • +1,除了我不同意“除非你有特定理由不这样做,否则总是立即自动释放”。您对此的理由(“很容易忘记编写release 消息”)现在不那么相关了,因为静态分析器很容易捕捉到这个错误。我个人避免使用自动释放对象,只是为了避免自动释放池混乱和耗尽非常大的自动释放池可能带来的冲击(是的,我已经注意到了)。我通常更喜欢通过尽快执行来分摊解除分配的成本。
    • 静态分析器可能需要一段时间才能在某些源代码上运行,因此您可能不希望它一直运行。耗尽自动释放池的大部分费用是释放对象——无论如何你都必须这样做。它脱颖而出的唯一原因是你一次释放它们全部(或几千个)而不是相隔几毫秒。
    • 在方法结束之前不要释放或自动释放。 -autorelease 之后的任何消息都可能会清空自动释放池。如果您要返回一个自动释放的对象,您不希望它在调用者得到它之前被销毁。
    • 有趣。这纯粹是理论上的,还是有一个真实的、工作的(即崩溃的)示例案例不会严重错误处理自动释放池?
    【解决方案2】:

    前者不一定适用于编译器,而是对字符串的建议,说明它如何能够优化存储其数据。这对 NSDictionary/NSArray/NSSet 最有用,have the ability to internally change their implementations depending on the size of their data set

    除此之外,您是对的:唯一的区别是所有权问题。我几乎从不使用WithCapacity 方法,而只是使用[NSMutableString string][NSMutableArray array],但IMO,这实际上只是风格问题,使用其中一个不会获得或失去任何东西。

    【讨论】:

    • 所以你个人会去分配并自己发布?
    • @fuzzygoat - 这取决于。我可以根据内存限制使用一个或另一个,无论我是在编写简洁的代码等等。
    【解决方案3】:

    第一个是自动释放的字符串。这将由系统在适当的时候释放。它被添加到自动释放池中,内存将由系统处理。一旦超出范围,您就无法保证它是有效的。如果此类型仅在您的方法中具有作用域,并且还用于从方法返回值,则此类型很有用。

    第二个被保留,因此引用计数为 1,并且不会添加到自动释放池中。您负责释放它并释放内存。如果要控制对象的范围,请使用此方法。用于成员变量等。

    我相信 2 行初始化只是样式,但我不会使用 2 行变体,因为您正在定义变量而不为其分配值,即使您在下一行。我猜这种镜像成员变量声明/初始化,但我个人不太喜欢。

    【讨论】:

      【解决方案4】:

      您提出了有效的问题。这真的取决于你在做什么,但对于一般的 iPhone 应用程序,我会说只使用第一个。当引用计数为 0 时,它会自动为您清理,您不必担心。

      当您确实有充分的理由自己管理字符串的内存时,请使用第二个。比如你想确定什么时候应该清理字符串,或者你希望在某个时间内存最小。

      如果你有充分的理由,我会说作为一般规则使用第二个。

      【讨论】:

        【解决方案5】:

        你的所有观点都是正确的!

        我不确定大小/容量提示的差异有多大,但更多信息肯定可以让运行时做出更好的决策。

        为什么要使用一种风格而不是另一种风格?那么,什么时候释放自动释放的对象?有两个不明显的原因可能很重要。首先,当一个方法使用大量内存时,您可以立即释放。 (我猜您也可以使用本地自动释放池。)其次,我发现使用自动释放可以隐藏内存泄漏并使调试某些代码更加困难。您的里程可能会因代码的年龄和质量而异。

        当我第一次开始开发 iPhone 应用程序时,我一直使用自动释放的对象。这很方便,因为我不完全了解它是如何工作的,而且它通常会做正确的事情。这些天来,我倾向于在手动释放内存方面犯错。当您真正了解引用计数的工作原理并在您不了解时立即强制解决问题时,这真的并不难。

        【讨论】:

        • “其次,我发现使用自动释放可以隐藏内存泄漏,并使调试某些代码更加困难。”我发现相反。如果您不立即发布某些内容(如在[[[… alloc] init] autorelease] 中),那么以后很容易忘记release 消息。
        • 另外,即使你记得写release消息,也很容易打不中。两种方式是提前返回 (if (!success) return NO;) 和抛出或传播的异常。
        • 我故意将其表述为“我发现”,因为我明白这完全是个人喜好。这样我犯的错误更少,因为它迫使我在编写代码时考虑变量的生命周期。正如我所说,您的里程可能会有所不同。
        【解决方案6】:

        如果您确定需要多长的字符串,请继续使用 -initWithCapacity: 方法。当你超过一个字符串的存储空间时,它会被重新分配和复制,这不是一个便宜的操作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多