NSString与NSMutableString的区别,以及使用stringByAppendingString方法

4

因此,我相当确定如果我经常计划操作字符串,例如使用 stringByAppendingString,我应该使用类型为 NSMutableString 的变量。

但是如果我正在做这样的事情呢?

UILabel *someLabel = [[UILabel alloc] init];
[someLabel setText: [[someDictionary objectForKey:@"some_key"] stringByAppendingString:@"some other string"];

我看到如果在NSString上使用stringByAppendingString,你最终会出现泄漏问题,因为与初始NSString相关联的指针会移动,指向由追加创建的新字符串,而对于NSMutableString,你的指针始终指向该可变字符串。所以我的问题是,在我调用stringByAppendingString时,如果它是一个字符串,但不是明确的NSString或NSMutableString,会发生什么隐式的情况?比如,在我的上面的例子中,字典中某个键的值。这样做是否有错,我应该像下面这样做?
[[[NSMutableString stringWithString:[someDictionary objectForKey:@"some_key"]] stringByAppendingString:@"some other string"]]
4个回答

5
我读到如果你在NSString上使用stringByAppendingString,就会出现泄漏,因为与初始NSString相关的指针会移动,指向由附加创建的新字符串,而对于NSMutableString,您的指针始终指向该可变字符串。听起来像是某人并没有完全理解内存管理的情况下给出的建议。当然,[NSString stringByAppendingString]返回一个新字符串。但是你如何处理这个新字符串取决于你。如果您粗心地将结果重新分配给保留的属性,则可能会导致内存泄漏,例如:
myStringProperty = [myStringProperty stringByAppendingString:@" more bits"];

正确的形式应该使用self,像这样:

正确的形式应该使用self,像这样:

self.myStringProperty = [myStringProperty stringByAppendingString:@" more bits"];

遵循Cocoa内存指南。

对于字典和其他集合类型:根据你所知道的类型适当地处理从字典中取出的内容。如果你取出的对象实际上是一个NSString,但试图将其用作NSMutableString,你的应用程序将会崩溃(显示“未找到选择器”或类似信息)。因此,在这种情况下,你确实需要从NSString创建一个新的NSMutableString。

有趣的一点是:苹果公司选择将NSMutableString作为NSString的子类。我认为这样做有些不明智--如果某个东西看起来是不可变的,因为它具有NSString类型,那么我希望它是不可变的! (但实际上它可能是NSMutableString)。与之相比,Java有一个String类和一个完全独立的BufferedString类。


NSMutableString不仅仅是NSString的一个专业化版本,它还打破了NSString的核心承诺之一。NSString的API表示NSString是不可变的。API上说“不可变”不仅意味着您不能更改内容;它还保证内容不会在背后更改。NSMutableString完全打破了这个承诺。两个类具有非常相似的常见功能并不意味着它们应该是超/子类相关的(尽管可能经常是这种情况),也不意味着这样做总是正确的。 - occulus
或者,换个角度来看:按照现有的方式,你可以传递一个NSString类型的东西,并且完全按照API文档中的约定进行处理,但是如果这个NSString类型的东西实际上不是NSMutableString的实例,那么你的程序可能会出现问题。这似乎并没有违反LSP(http://en.wikipedia.org/wiki/Liskov_substitution_principle),但在我看来仍然不正确。你必须担心一个类型实际上是否是某个子类型,这是一种不好的“OO气味”。 - occulus
@wayne-hartman 我完全没有对 oC 的动态本质或程序员所拥有的力量有任何问题。但是,权力也伴随着责任。例如,在我的时间里,我写了很多类别,但没有一个类别会否定我分类的合同。如果我这样做了,然后其他人的代码出了问题,我不会责怪其他代码的作者,我会责怪自己。 - occulus
@occulus,请问您能否详细说明一下self.myStringProperty和myStringProperty之间的区别? - S.J
1
@S.J self.myStringProperty使用点符号访问该属性的getter或setter方法。如果省略self.部分,则直接访问该属性后面的类值。如果您正在使用ARC,则这比以前不那么重要,因为ARC处理了以前在setter方法中更明确的内存管理。 - occulus
显示剩余2条评论

3

-stringByAppendingString方法将返回一个新的NSString,它与涉及的两个字符串都不同。换句话说:

NSString *string3 = [string1 stringByAppendingString:string2];

string3是全新的字符串。string1完全没有被改变,它的内存位置和内容都没有发生任何变化。告诉你这个的人可能只是误解了实际情况。

[mutableString1 appendString:string2];

在这种情况下,mutableString1仍然指向同一对象,但该对象的内容已被更改以包括string2。
最后要记住的一件事是,如果您使用可变字符串,则应小心共享对它的引用。如果将可变字符串传递给某个函数,该函数保留对该可变字符串的指针,然后您的代码在将来的某个时间更改了该可变字符串,则其他引用也指向完全相同的对象,这意味着其他代码也会看到更改。如果那是您想要的,那很好,但如果不是,您必须小心。
避免此问题的一种方法是将NSString的@property语句声明为“copy”而不是“retain”。这将在设置属性之前复制可变字符串,并且-copy方法会隐式地为您提供一个非可变版本,因此它将创建NSMutableString的NSString副本。

3

我一直是[NSString stringWithFormat@"%@%@", a, b];的粉丝,因为这样你可以清楚地得到一个新的自动释放字符串,并且可以正确处理“a”和“b”。

使用[someDictionary objectForKey:@"some_key"],您将获得最初放入该字典中的对象类型。因此,如果不知道字典中有什么,盲目调用stringByAppendingString似乎是个坏主意。


1
啊,对了,我忘记了这个方法。我同意,它非常简单明了,你总是知道会发生什么。我认为这对于我正在尝试做的事情来说是最好的选择。谢谢! - Joseph

0

如果您遵循内存管理规则,使用stringByAppendingString时就不会有问题。简而言之:

  1. 如果您拥有一个对象,您需要在某个时候释放或自动释放它。
  2. 如果您使用alloc、new或copy方法创建对象,或者保留了它,那么您就拥有该对象。

确保您阅读了苹果的内存管理规则

在您问题中的第一个代码示例中,您没有在任何涉及的NSString上使用alloc、new、copy或retain,因此您不需要做任何事情来释放它。如果在示例中未包含的代码之外,您正在使用alloc、new、copy或retain在任何NSString上,那么您需要确保稍后释放它们。


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