关于iphone:NSMutableString作为保留/复制

NSMutableString as retain/copy

我的应用程序中有aa个nsmutableString(几乎10-11个);都定义为ivar/属性

1
@property (nonatomic, retain) NSMutableString *str1;

我在某个地方读到,最好用"copy"作为字符串。是真的吗?如果是,我可以在我的应用程序中替换retain to copy并删除dealloc中的版本吗?

我还需要考虑其他的事情吗?

另外,在一个应用程序中有10-11个nsmutablestring是正常的吗?我的意思是从内存使用的角度来看?我的应用程序中也有4-5个nsmutabledictionary。如果可以的话请告诉我。


(注:NSmutableString特定的响应是一种向下的方式)好的。

I have aa number of NSMutableString's in my app (almost 10-11); all defined as ivar/property

Ok.

@property (nonatomic, retain) NSMutableString *str1;
I read somewhere that it is better to use"copy" for strings. Is that true?

Ok.

是的,复制nsstring(还没有讨论nsmutablestring)应该是默认值。原因(如其他海报所述)是,在实施copyWithZone:时,不可变实例的实现可以简单地由retain本身完成。另外,copy为您提供了预期的行为。换句话说,如果一个字符串实际上是不可变的,就不需要保留它。为了保证处理的是不可变的字符串,只需在创建和设置时进行复制。好的。

If yes, can I just replace retain to copy in my app and remove the release in dealloc?

Ok.

copyretain一样返回一个对象,您必须在非垃圾收集环境中(例如,在dealoc中)显式释放该对象。不可变字符串仍被引用计数。但也有例外,特别是:好的。

  • 在程序的持续时间内存在cf/ns字符串文本好的。

  • 您可以使用CFAPI创建从不释放的字符串,或者在传统保留/释放机制范围之外进行管理的字符串。好的。

  • NS类型的子类可以实现另一个方案(尽管通常考虑这一点是有缺陷的)好的。

Do I need to consider some other things as well?

Ok.

现在我们来了解一下NSmutable类型的详细信息。属性语法的实现调用copy。如我们所知,-[NSMutableString copy]返回一个不变的nsstring。这是一个等待简单实现发生的问题,因为您的nsmutableString ivar现在是一个nsstring,使用默认的合成setter实现。好的。

1
2
3
@property (nonatomic, retain) NSMutableString *str1;`
// or
@property (nonatomic, copy) NSMutableString *str1;`

声明时:好的。

1
 @property (nonatomic, retain) NSMutableString *str1;`

您可以使用默认的合成实现。好的。

但在声明时有一个转折点:好的。

1
 @property (nonatomic, copy) NSMutableString *str1;`

您不应该使用默认的合成实现。相反,你应该自己写:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)setStr1:(NSMutableString *)arg
{
    /* locking/observing/undo/etc omitted */
    NSMutableString * copy = [arg mutableCopy];
    NSMutableString * prev = str1;
    str1 = copy;
    [prev release];
}

- (NSMutableString *)str1
{
  /* locking/observing/etc omitted */
  /* don't return something the clients thinks they may
     possibly modify, and don't return something you may
     modify behind their back
  */

    return [[str1 mutableCopy] autorelease];
  // -- or???
    return [[str1 retain] autorelease];
}

隐马尔可夫模型。。。这不是很清楚,对客户也不是很体贴。它还暗示了许多不必要的事情,并引入了额外的复制开销。让我们重新评估一下。好的。

此时的其他注意事项/问题:好的。

  • 默认的setter要求客户机对字符串进行可变的复制(如果它们只保存不可变的字符串)。在setter中,这会立即变成一个惟一的副本,因为您不能期望客户机只为您创建一个惟一的可变副本——此外,这是类实现的职责。所以nsmutablestring对于setter来说是一个错误的参数。nsstring是适当的setter,除非您保留字符串。好的。

  • nsmutableString也是一个不明确的getter——请参见实现。它是复制+自动释放还是保留+自动释放?一般形式没有标准的期望。此外,客户可能依赖于一种特定的行为。这实际上是类的实现细节,除非您处理的是紧密耦合的对象。紧耦合对象是那些基本上是相互依赖的对象,在其他上下文中通常不可重用。这个概念很好,您可能只是出于几个原因使用了一个私有类。在这两种情况下,对外部客户机和子类的复制/写入语义都不清楚(隐含地),并且实现是危险的。好的。

  • 共享一个不可变的字符串通常是一个糟糕的设计。nsmutableString不是线程安全的。客户机访问和改变字符串是没有意义的——这很危险。任何共享非线程安全对象的设计都应该小心考虑。在这种情况下,共享还扩展到子类的使用(因此,尽可能将其设置为@private)好的。

  • RETAIN通常也是一种糟糕的设计——客户机不知道字符串是在什么时候(或正在)被外部修改的,除非您添加等效的外部代码来支持它。好的。

  • 根据接口的使用方式,这也会引入大量不必要的复制。喝倒采。好的。

好吧-现在我们非常确信我们的"改进"在大多数情况下仍然是一个坏主意=)我们如何改进设计?好的。

除了紧耦合对象的异常情况外,您还应该按以下方式声明/实现您的属性:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@interface MONThing : NSObject
{
@private
    NSMutableString * str1;
}

/* note: passes/returns NSString */
@property (nonatomic, copy) NSString * str1;

@end

@implementation MONThing

// no need for clients to create a mutableCopy
- (void)setStr1:(NSString *)arg
{
  /* locking/observing/undo/etc omitted */
    NSMutableString * copy = [arg mutableCopy];
    NSMutableString * prev = str1;
    str1 = copy;
    [prev release];
}

// the result is clearly defined. return a copy for
// thread-safety, expected behavior, and to minimize
// further copies when handling the result.
- (NSString *)str1
{
  /* locking/observing/etc omitted */
  /* don't return something the clients thinks they
     may possibly modify, and don't return something
     you may modify behind their back
  */

    return [[str1 copy] autorelease];
}

@end

您的界面现在得到了改进,更加正确,需要的文档更少,性能更好。好的。

如果不需要访问器,也可以删除公开可见的访问器。好的。

我们可以在需要的地方使用这个接口。好的。

但还有更多!事实证明,界面的必要性比许多人想象的要少。在大多数情况下,我们可以通过在可能的情况下避免将nsmutatablestring用作IVAR来完全避免许多问题。好的。

如前所述,nsmutablestring不是线程安全的,在许多情况下,当字符串很小或不经常更改时,使用(复制的)nsstring ivar更容易、更高效。通过将可变字符串添加到接口(如上所述),您必须手动确保线程安全。在许多情况下,最好是沿着这条线做一些事情:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@interface MONThing : NSObject
{
    NSString * str1;
}

@property (nonatomic, copy) NSString * str1;

@end

@implementation MONThing

@synthesize str1;

- (void)updateTimeElapsed:(NSTimeInterval)seconds
{
    NSMutableString * s = [NSMutableString stringWithFormat:@"%f seconds", seconds];
    /* ...some mutations... */
    self.str1 = s;
}

@end

当然,也会有一些例外——您需要一个可变的ivar,但最好在可能的情况下和有疑问的情况下使用不可变的ivar,而不是引入大量线程复杂性。在大多数情况下,您可以从接口中删除可变字符串的使用,并在必要时构建字符串(如上所示)。好的。

(nsmutablearray馆藏,nsmutabledictionary,nsmutableset等),在其他的手上,或是在需要更多的实现等,是有趣和复杂的将军。

好。

好运气!

好。 好的。


虽然taskinor的回答是正确的,但我想再解释一下。

建议将副本用于属于具有可变/不可变对的类群的类,如NSString/NSMutableStringNSArray/NSMutableArrayNSDictionary/NSMutableDictionaryNSSet/NSMutableSet

这样做的原因是,可能有一个属性声明为不可变类型(如nsstring),但却传递给它一个可变类型(如nsmutablestring)。在这种情况下,可以按照taskinor的描述从类外部更改属性。

建议使用copy,因为它在类集群中的行为非常敏感。向可变类发送copy将返回对象的不可变副本(即向NSMutableString发送copy消息将返回NSString)。但是,将copy发送给不变的对应方就相当于向它发送retain消息。


对于可变字符串,复制和保留具有不同的结果。如果您将str1复制到str2,那么对str1的任何更改都不会反映在str2中,对str2的任何更改也不会反映在str1中,因为它们是独立的对象。但如果str1保留到str2,则两者都引用相同的可变字符串对象,而通过str1进行的更改将反映在str2中。但是nsstring不是可变的。因此,对于简单字符串(即,不是可变字符串),复制只是增加引用计数,这意味着它在内部被视为保留(对于非可变对象,我不完全确定是否将复制视为保留,但在某些不同的问题中看到了这一点)。谷歌,我会编辑,如果我能找到链接)。

如果您使用copy,那么您仍然需要在dealloc中释放它。

拥有10-11个正常大小的字符串对内存使用没有负面影响。一个典型的应用程序可能包含更多的字符串。按正常大小,我的意思是你不会在一个字符串中附加100万个字符。那将是一个严重的问题。字符串占用的内存将随着字符串长度的增加而增加。字典也是如此。字典所占用的内存将取决于您在字典中存储的内容。字典本身没有太多开销。你可以随时使用仪器检查你的应用程序的内存使用情况。