问题描述
我的应用中有许多 NSMutableString(几乎 10-11 个);全部定义为 ivar/property
I have aa number of NSMutableString's in my app (almost 10-11); all defined as ivar/property
@property (nonatomic, retain) NSMutableString *str1;
我在某处读到最好对字符串使用复制".真的吗?如果是,我可以在我的应用程序中将保留替换为复制并删除 dealloc 中的发布吗?
I read somewhere that it is better to use "copy" for strings. Is that true? If yes, can I just replace retain to copy in my app and remove the release in dealloc ?
我还需要考虑其他一些事情吗?
Do I need to consider some other things as well ?
此外,在 1 个应用程序中有 10-11 个 NSMutableString 是否正常.我的意思是从内存使用的角度来看?我的应用程序中也有 4-5 个 NSMutableDictionary.请让我知道是否可以.
Also, is it normal to have 10-11 NSMutableString's in 1 app..I mean from memory usage perspective ? I also have 4-5 NSMutableDictionary's as well in my app. Please let me know if that is fine.
推荐答案
(注意:NSMutableString 特定的响应是一种方式)
(note: the NSMutableString specific response is a ways down)
我的应用中有许多 NSMutableString(几乎 10-11 个);全部定义为 ivar/property
@property (nonatomic, retain) NSMutableString *str1;
我在某处读到最好对字符串使用复制".是真的吗?
@property (nonatomic, retain) NSMutableString *str1;
I read somewhere that it is better to use "copy" for strings. Is that true?
是的,复制 NSString(还没有谈论 NSMutableString)应该是默认的.原因(如其他海报所述)是不可变实例的实现可以简单地在 copyWithZone:
的实现中retain
自身.此外, copy
为您提供了预期的行为.换句话说,如果字符串实际上是不可变的,则保留字符串是没有用的.为了保证您正在处理一个不可变的字符串,您只需在创建和设置时进行复制.
yes, copying NSString (not talking about NSMutableString quite yet) should be the default. the reason (as stated by other posters) is that the implementation of an immutable instance can simply retain
itself in its implementation of copyWithZone:
. also, copy
gives you the expected behavior. in other words, there is not a use for retaining a string if it is in fact immutable. in order to guarantee that you're dealing with an immutable string, you simply copy at creation and set.
如果是,我可以在我的应用程序中将retain替换为copy并在dealloc中删除release吗?
copy
,像 retain
返回一个对象,你必须在非垃圾收集环境中明确释放(例如,在 dealloc 中).不可变字符串仍然是引用计数的.也有例外,特别是:
copy
, like retain
returns an object which you must explicitly release (e.g., in dealloc) in non-garbage collected environments. immutable strings are still reference-counted. there are exceptions to this, notably:
CF/NS-String 文字在程序的持续时间内存在
CF/NS-String literals exist for the program's duration
您可以使用 CF-API 来创建永远不会被释放的字符串,或者在传统的保留/释放机制的范围之外进行管理
you can use CF-APIs to create strings which are never released, or are managed beyond the scope of traditional retain/release mechanisms
NS-Type 子类可以实现另一种方案(尽管通常对此的考虑是有缺陷的)
NS-Type subclasses could implement another scheme (although usual considerations of this are flawed)
我还需要考虑其他一些事情吗?
现在我们了解 NSMutable 类型的详细信息.属性语法的实现调用了copy
.正如我们所知,-[NSMutableString copy]
返回一个不可变的 NSString.这是一个等待天真的实现发生的问题,因为您的 NSMutableString ivar 现在是一个 NSString,使用默认合成的 setter 实现.
now we get to the NSMutable-type details. the implementation of property syntax invokes copy
. as we know, -[NSMutableString copy]
returns an immutable NSString. this is an issue waiting to happen for a naive implementation, since your NSMutableString ivar is now an NSString, using default-synthesized setter implementation.
@property (nonatomic, retain) NSMutableString *str1;`
// or
@property (nonatomic, copy) NSMutableString *str1;`
声明时:
@property (nonatomic, retain) NSMutableString *str1;`
您可以使用默认的综合实现.
you may use the default synthesized-implementation.
但是声明时有一个转折:
but there's a twist when declaring:
@property (nonatomic, copy) NSMutableString *str1;`
你应该不使用默认的合成实现.相反,您应该自己编写:
you should not use the default synthesized-implementation. instead, you should write your own:
- (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];
}
嗯……这不是很清楚,也不是很体贴客户.它还意味着很多不必要的事情,并引入了额外的复制开销.让我们重新评估一下.
hmm... that's not very clear, and not very considerate for clients. it also implies a lot of unnecessary things, and introduces additional copy-overhead. let's re-evaluate this.
此时的其他注意事项/问题:
additional considerations/problems at this point:
default-setter 要求客户端制作字符串的 mutableCopy,如果它们只保存一个不可变的字符串.在 setter 中,您会立即将其转换为唯一副本,因为您不能期望客户端为您创建唯一的 mutableCopy —— 此外,这是类实现的职责.所以 NSMutableString 对 setter 来说是一个糟糕的参数.NSString 是合适的设置器,除非您保留字符串.
the default-setter requires that the client makes a mutableCopy of the string, if they only hold an immutable string. that is immediately turned into a unique copy by you, in the setter, since you can't expect the client to create a unique mutableCopy just for you -- besides, that's the duty of class's implementation. so NSMutableString is a bad argument for the setter. NSString is the appropriate setter, unless you're retaining the string.
NSMutableString 也是一个模棱两可的 getter——参见实现.它是复制+自动释放还是保留+自动释放?对于一般形式没有标准的期望.此外,客户可能依赖于一种特定的行为.这确实是该类的一个实现细节,除非您正在处理紧密耦合的对象.紧密耦合的对象是那些基本上相互依赖的对象,并且通常不能在其他上下文中重用.这个概念很好,您可能只是出于多种原因使用私有类.在任何一种情况下,对外部客户端和子类的复制/写入语义都不是(隐式)清晰,而且实现是危险的.
NSMutableString is also an ambiguous getter -- see the implementation. does it copy+autorelease or retain+autorelease? there isn't a standard expectation for the general form. also, clients may depend on one specific behavior. this really is an implementation detail of the class, unless you're dealing with tightly coupled objects. tightly coupled objects are those which are basically co-dependent, and not generally reusable in other contexts. that concept's fine, you may just be using a private class for several reasons. in either case, copy/write semantics to external clients and subclasses is not (implicitly) clear, and the implementation is dangerous.
共享一个 NSMutableString 通常是一个糟糕的设计.NSMutableString 不是线程安全的.客户端访问和改变字符串是没有意义的 - 这很危险.任何包含非线程安全共享对象的设计都应谨慎考虑.在这种情况下,共享也扩展到子类的使用(所以尽可能使用 @private
)
sharing an NSMutableString is generally a bad design. NSMutableString is not thread-safe. it makes no sense for clients to access and mutate the string - it's dangerous. any design which shared objects which are not thread safe should be considered with care. sharing in this case also extends to subclass usage (so make it @private
, wherever possible)
retain 通常也是一个糟糕的设计——客户端不会知道字符串何时被(或正在)外部修改,除非您添加等效的外部代码来支持.
retain is also generally a bad design -- the clients are not going to know when the string's been (or is being) modified externally, unless you add equivalent external code to support that.
根据接口的使用方式,这也会引入很多不必要的复制.嘘.
depending on how the interface is used, this can also introduce a lot of unnecessary copying. boo.
好的 - 现在我们非常确信我们的改进"在大多数情况下仍然是一个坏主意 =) 我们如何改进设计?
ok - now we're pretty convinced our 'improvement' is still a bad idea in most cases =) how can we improve the design?
除了紧耦合对象的特殊情况外,您应该按如下方式声明/实现您的属性:
you should, apart from exceptional cases of tightly coupled objects, declare/implement your property as follows:
@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
您的界面现在得到了改进,更加正确,需要的文档更少,性能更好.
your interface is now improved, more correct, requires less documentation and performs better.
如果您不需要它们,您也可以删除公开可见的访问器.
you can also remove publicly visible accessors if you don't need them.
我们可以在需要时使用该界面.
we can live with that interface, where it is required.
但还有更多!事实证明,界面的必要性比许多人想象的要少.在大多数情况下,我们可以通过简单地避免将 NSMutableString 用作 ivar 来完全避免许多问题.
but there's more! as it turns out, the interface is necessary less often than many people think. we can avoid many of the issues altogether in most cases by simply avoiding use of NSMutableString as an ivar where possible.
如前所述,NSMutableString 不是线程安全的,在许多情况下,当您的字符串很小或不经常更改时,使用(复制的)NSString ivar 更容易、更有效.通过将可变字符串添加到接口(如上),您必须手动保证线程安全.在许多情况下,最好按照以下方式进行操作:
as mentioned earlier, NSMutableString is not thread-safe, it's easier and more efficient in many cases to use a (copied) NSString ivar when your string is small, or does not change frequently. by adding the mutable string to the interface (as above), you'll have to guarantee thread-safety manually. in many cases, it is optimal to do something along these lines:
@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,但最好在可能的情况下使用不可变的 ivars,并且在有疑问的情况下,而不是引入一个大量的线程复杂性.在大多数情况下,您可以从接口中删除可变字符串的使用,并在必要时构建字符串(如上所示).
of course, there will be a few exceptions to this -- where you will need a mutable ivar, but it's best to use immutable ivars where possible, and where in doubt, rather than introducing a ton of threading complexities. in most cases, you can remove the use of mutable strings from interfaces, and build the strings when necessary (as demonstrated above).
另一方面,集合(NSMutableArray、NSMutableDictionary、NSMutableSet 等)在实现中更频繁地需要,并且通常更复杂.
collections (NSMutableArray, NSMutableDictionary, NSMutableSet, etc), on the other hand, are required more often in implementations, and are more complex in general.
祝你好运!
这篇关于NSMutableString 作为保留/复制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!