tes*_*dtv 18 iphone cocoa-touch copy objective-c
我的应用程序中有很多NSMutableString(差不多10-11); 全部定义为ivar/property
@property (nonatomic, retain) NSMutableString *str1;
Run Code Online (Sandbox Code Playgroud)
我在某处读到,最好对字符串使用"copy".真的吗?如果是,我可以在我的应用程序中替换retain复制并删除dealloc中的版本吗?
我还需要考虑其他一些事情吗?
此外,在1个应用程序中拥有10-11 NSMutableString是正常的吗?我的意思是从内存使用角度来看?我的应用程序中也有4-5个NSMutableDictionary.如果没问题,请告诉我.
Abi*_*ern 27
虽然Taskinoor的答案是正确的,但我想补充一点解释.
建议使用副本作为具有可变/不可变对的类集群的一部分; 比如NSString/ NSMutableString NSArray/ NSMutableArray NSDictionary/ NSMutableDictionary NSSet/NSMutableSet
这样做的原因是可以将一个属性声明为不可变类型(如NSString),然后将其传递给一个可变类型(如NSMutableString).在这种情况下,可以像Taskinoor描述的那样在类外部更改属性.
使用copy建议,因为它与类簇理智的行为.发送copy到一个可变类返回对象(即发送的不可变拷贝copy消息到NSMutableString返回一个NSString).但是,向copy不可变对应方发送相当于向其发送retain消息.
jus*_*tin 27
(注意:NSMutableString特定的响应是一种方式)
我的应用程序中有很多NSMutableString(差不多10-11); 全部定义为ivar/property
@property (nonatomic, retain) NSMutableString *str1;我在某处读到,最好对字符串使用"copy".真的吗?
是的,复制NSString(还没有谈到NSMutableString)应该是默认值.原因(如其他海报所述)是不可变实例的实现可以简单地retain在其实现中实现copyWithZone:.另外,copy给你预期的行为.换句话说,如果字符串实际上是不可变的,则没有用于保留字符串的用法.为了保证您处理不可变的字符串,您只需在创建时进行复制并进行设置.
如果是,我可以在我的应用程序中替换retain复制并删除dealloc中的版本吗?
copy,就像retain返回一个必须在非垃圾收集环境中明确释放(例如,在dealloc中)的对象.不可变字符串仍然是引用计数的.这有例外,特别是:
CF/NS-String文字存在于程序的持续时间内
您可以使用CF-API创建从未发布的字符串,或者管理超出传统保留/释放机制范围的字符串
NS-Type子类可以实现另一种方案(虽然通常考虑这个有缺陷)
我还需要考虑其他一些事情吗?
现在我们来看看NSMutable类型的细节.属性语法的实现调用copy.我们知道,-[NSMutableString copy]返回一个不可变的NSString.这是一个等待天真实现的问题,因为你的NSMutableString ivar现在是一个NSString,使用默认合成的setter实现.
@property (nonatomic, retain) NSMutableString *str1;`
// or
@property (nonatomic, copy) NSMutableString *str1;`
Run Code Online (Sandbox Code Playgroud)
宣布时:
@property (nonatomic, retain) NSMutableString *str1;`
Run Code Online (Sandbox Code Playgroud)
您可以使用默认的合成实现.
但声明时有一个转折:
@property (nonatomic, copy) NSMutableString *str1;`
Run Code Online (Sandbox Code Playgroud)
你应该不使用默认的合成的实现.相反,你应该写自己的:
- (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];
}
Run Code Online (Sandbox Code Playgroud)
嗯......这不是很清楚,也不是很贴心客户.它还意味着许多不必要的东西,并引入了额外的复制开销.让我们重新评估一下.
此时的其他注意事项/问题:
default-setter要求客户端生成字符串的mutableCopy,如果它们只包含一个不可变的字符串.你可以在setter中立即将它变成一个独特的副本,因为你不能指望客户只为你创建一个独特的mutableCopy - 此外,这是类实现的责任.所以NSMutableString是setter的一个坏参数.NSString是合适的setter,除非你保留字符串.
NSMutableString也是一个模糊的getter - 请参阅实现.它复制+自动释放还是保留+自动释放?一般形式没有标准期望.此外,客户可能依赖于一种特定的行为.这真的是类的实现细节,除非你正在处理紧耦合的对象.紧密耦合的对象是基本上相互依赖的对象,并且通常在其他情况下不可重用.这个概念很好,你可能只是因为几个原因而使用私人课程.在任何一种情况下,对外部客户端和子类的复制/写入语义都不是(隐式)清除,并且实现是危险的.
共享NSMutableString通常是一个糟糕的设计.NSMutableString不是线程安全的.客户端访问和改变字符串是没有意义的 - 这很危险.任何非线程安全共享对象的设计都应谨慎考虑.在这种情况下共享也扩展到子类使用(所以@private尽可能使它)
retain通常也是一个糟糕的设计 - 客户端不会知道字符串何时(或正在)被外部修改,除非您添加等效的外部代码来支持它.
根据接口的使用方式,这也会引入大量不必要的复制.嘘.
好的 - 现在我们非常确信在大多数情况下我们的'改进'仍然是一个坏主意=)我们如何改进设计?
除了紧密耦合对象的特殊情况之外,您应该声明/实现您的属性,如下所示:
@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
Run Code Online (Sandbox Code Playgroud)
您的界面现在得到了改进,更加正确,需要的文档更少,性能更好.
如果您不需要,也可以删除公开可见的访问者.
我们可以在需要的地方使用该界面.
但还有更多!事实证明,界面比许多人想象的更少.在大多数情况下,我们可以通过简单地避免在可能的情况下使用NSMutableString作为ivar来避免许多问题.
如前所述,NSMutableString不是线程安全的,在你的字符串很小或不经常更改的情况下,在许多情况下使用(复制的)NSString ivar会更容易和更有效.通过将可变字符串添加到接口(如上所述),您必须手动保证线程安全.在许多情况下,沿着这些方向做某事是最佳的:
@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
Run Code Online (Sandbox Code Playgroud)
当然,会有一些例外 - 你需要一个可变的ivar,但最好尽可能使用不可变的ivars,如果有疑问,而不是引入大量的线程复杂性.在大多数情况下,您可以从接口中删除可变字符串的使用,并在必要时构建字符串(如上所示).
另一方面,集合(NSMutableArray,NSMutableDictionary,NSMutableSet等)在实现中更经常需要,并且通常更复杂.
祝好运!
tas*_*oor 12
对于可变字符串复制和保留有不同的结果.如果你将str1的副本复制到str2,那么对str1的任何更改都不会反映在str2中,并且str2中的任何更改都不会反映在str1中,因为它们是单独的对象.但是如果str1保留为str2,则两者都指向相同的可变字符串对象,并且通过str1的更改将反映在str2中.但NSString不可变.因此,对于简单的字符串(即,不是可变字符串),复制只是增加了引用计数,这意味着它在内部被视为保留(我不是100%确定处理副本,如保留非可变对象,但看到这个所以在一些不同的问题.谷歌搜索,我会编辑,如果我能找到链接.).
如果你使用副本,那么你仍然需要在dealloc中释放它.
具有10 - 11正常大小的字符串应该对内存使用没有负面影响.典型的应用程序可能包含更多字符串.按正常大小我的意思是你没有在字符串中附加100万个字符.那将是一个严重的问题.字符串占用的内存将随着字符串长度的增加而增加.字典也一样.字典占用的内存取决于您在字典中存储的内容.字典本身没有太多开销.您始终可以使用仪器检查应用的内存使用情况.