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)应该是默认值。原因(如其他海报所述)是,在实施
If yes, can I just replace retain to copy in my app and remove the release in dealloc?
Ok.
在程序的持续时间内存在cf/ns字符串文本好的。
您可以使用CFAPI创建从不释放的字符串,或者在传统保留/释放机制范围之外进行管理的字符串。好的。
NS类型的子类可以实现另一个方案(尽管通常考虑这一点是有缺陷的)好的。
Do I need to consider some other things as well?
Ok.
现在我们来了解一下NSmutable类型的详细信息。属性语法的实现调用
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),但却传递给它一个可变类型(如nsmutablestring)。在这种情况下,可以按照taskinor的描述从类外部更改属性。
建议使用
对于可变字符串,复制和保留具有不同的结果。如果您将str1复制到str2,那么对str1的任何更改都不会反映在str2中,对str2的任何更改也不会反映在str1中,因为它们是独立的对象。但如果str1保留到str2,则两者都引用相同的可变字符串对象,而通过str1进行的更改将反映在str2中。但是nsstring不是可变的。因此,对于简单字符串(即,不是可变字符串),复制只是增加引用计数,这意味着它在内部被视为保留(对于非可变对象,我不完全确定是否将复制视为保留,但在某些不同的问题中看到了这一点)。谷歌,我会编辑,如果我能找到链接)。
如果您使用copy,那么您仍然需要在dealloc中释放它。
拥有10-11个正常大小的字符串对内存使用没有负面影响。一个典型的应用程序可能包含更多的字符串。按正常大小,我的意思是你不会在一个字符串中附加100万个字符。那将是一个严重的问题。字符串占用的内存将随着字符串长度的增加而增加。字典也是如此。字典所占用的内存将取决于您在字典中存储的内容。字典本身没有太多开销。你可以随时使用仪器检查你的应用程序的内存使用情况。