8

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

NSMutableString *newPath = [NSMutableString stringWithCapacity:42];

或者

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

编辑...还

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

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

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

4

6 回答 6

16
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。(但是,请确保您对队列一次运行的操作数设置了限制——否则,您可能更容易陷入分页地狱。)

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

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

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

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

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

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

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

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

于 2009-09-28T17:15:25.907 回答
4

前者不一定适用于编译器,而是对字符串的建议,说明它如何能够优化存储其数据。这对于 NSDictionary/NSArray/NSSet 最有用,它们能够根据数据集的大小在内部更改其实现

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

于 2009-09-28T15:38:34.623 回答
2

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

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

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

于 2009-09-28T15:58:42.950 回答
1

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

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

当你有充分的理由这样做时,我会说作为一般规则使用第二个。

于 2009-09-28T15:40:57.943 回答
1

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

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

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

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

于 2009-09-28T16:17:58.193 回答
0

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

于 2009-09-28T17:11:51.703 回答