本文介绍了NSMutableString作为保留/复制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个NSMutableString的数字在我的应用程序(几乎10-11);所有定义为ivar / property

  @property(nonatomic,retain)NSMutableString * str1; 

我在某处读到最好对字符串使用copy。真的吗?如果是,我可以只是替换保留在我的应用程序中复制,并删除dealloc释放?



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



11 NSMutableString's in 1 app..I表示从内存使用角度?我也有4-5 NSMutableDictionary的以及在我的应用程序。

解决方案

(注意:NSMutableString具体响应是一种方式)

是的,复制NSString(还没有谈到NSMutableString)应该是默认的。 (如其他海报所说)的原因是不可变实例的实现可以在其实现 copyWithZone:中简单地 retain $ c>。此外, copy 可以提供预期的行为。换句话说,如果字符串实际上是不可变的,则不存在用于保留该字符串的用途。为了保证你正在处理一个不可变的字符串,你只需在创建和设置时复制。

>,如 retain 返回一个对象,您必须在非垃圾收集环境中显式释放(例如,在dealloc中)。不可变字符串仍然被引用计数。有一些例外,特别是:




  • 程序持续时间存在CF / NS-String文字

    li>
  • 您可以使用CF-API创建从未发布或超出传统保留/发布机制范围的字符串





现在我们得到NSMutable类型的细节。属性语法的实现调用 copy 。我们知道, - [NSMutableString copy] 返回一个不可变的NSString。这是一个等待发生的天真的实现的问题,因为您的NSMutableString ivar现在是一个NSString,使用默认合成setter实现。

  @property(nonatomic,retain)NSMutableString * str1;`
//或
@property(nonatomic,copy)NSMutableString * str1;`
pre>

当声明:

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

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



但是有一个扭曲,当声明:

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

您应该不使用默认的合成实现。

   - (void)setStr1:(NSMutableString *)arg 
{
/ * lock / observe / undo / etc ignored * /
NSMutableString * copy = [arg mutableCopy];
NSMutableString * prev = str1;
str1 = copy;
[prev release];
}

- (NSMutableString *)str1
{
/ *锁定/观察/ etc省略* /
/ *不返回客户认为他们可能
可能修改,不返回可能
修改背面
* /
return [[str1 mutableCopy] autorelease];
// - 或???
return [[str1 retain] autorelease];
}

hmm ...这不是很清楚,它也意味着很多不必要的东西,并引入额外的副本开销。让我们重新评估一下。




  • >

    default-setter要求客户端对字符串进行mutableCopy,如果它们只包含一个不可变的字符串。它会立即变成一个唯一的副本由你,在setter,因为你不能指望客户端创建一个唯一的mutableCopy只为你 - 此外,这是类的实现的义务。所以NSMutableString是一个错误的参数为setter。 NSString是适当的setter,除非你保留字符串。


  • NSMutableString也是一个模糊的getter - 它复制+ autorelease或保留+ autorelease?没有一般形式的标准期望。而且,客户端可能依赖于一个特定的行为。这真的是类的实现细节,除非你处理紧耦合对象。紧密耦合的对象是基本上共同依赖的,并且在其他上下文中通常不可重复使用的对象。那个概念很好,你可能只是使用一个私人类有几个原因。


  • 共享一个NSMutableString通常是一个非常简单的方法,坏设计。 NSMutableString不是线程安全的。它对客户端访问和改变字符串没有任何意义 - 这是危险的。任何共享不是线程安全的对象的设计都应该小心考虑。共享在这种情况下也扩展到子类的使用(所以使它 @private ,尽可能)


  • 保留通常也是一个糟糕的设计 - 客户端不知道字符串何时被外部修改,除非您添加等效的外部代码来支持。


  • 取决于如何使用界面,这也可能引入大量不必要的复制。嘘。




好 - 现在我们相信我们的改进在大多数情况下仍然是个坏主意= )我们如何改进设计?



除了紧密耦合对象的特殊情况,你应该声明/实现你的属性如下:

  @interface MONThing:NSObject 
{
@private
NSMutableString * str1;
}

/ *注意:传递/返回NSString * /
@property(nonatomic,copy)NSString * str1;

@end

@implementation MONThing

//无需客户端创建mutableCopy
- (void)setStr1: NSString *)arg
{
/ * lock / observe / undo / etc omitted * /
NSMutableString * copy = [arg mutableCopy];
NSMutableString * prev = str1;
str1 = copy;
[prev release];
}

//结果清楚定义。返回
//线程安全,预期行为的副本,并在处理结果时最小化
//进一步的副本。
- (NSString *)str1
{
/ *锁定/观察/ etc省略* /
/ *不返回客户认为他们
可能修改,不返回东西
你可以在他们的后面修改
* /
return [[str1 copy] autorelease];
}

@end

更正确,需要的文档更少,效果更好。



如果您不需要它们,也可以删除公开的访问者。



我们可以使用该界面,它是必需的。



但还有更多!事实证明,接口的必要性比很多人想象的要少。我们可以在大多数情况下通过简单地避免使用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]
/ * ...一些突变... * /
self.str1 = s;
}

@end

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



集合(NSMutableArray,NSMutableDictionary,NSMutableSet,等等),另一方面,在实现中更常需要,并且在一般上更复杂。



好运!


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

@property (nonatomic, retain) NSMutableString *str1;

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 ?

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.

解决方案

(note: the NSMutableString specific response is a ways down)

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.

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 literals exist for the program's duration

  • 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 subclasses could implement another scheme (although usual considerations of this are flawed)

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;`

when declaring:

 @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:

  • 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 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.

  • 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 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.

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.

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

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).

collections (NSMutableArray, NSMutableDictionary, NSMutableSet, etc), on the other hand, are required more often in implementations, and are more complex in general.

good luck!

这篇关于NSMutableString作为保留/复制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 05:31