在Objective C中有NSArray,NSDictionary等类的可变和不可变的版本背后的逻辑是什么?

为什么像Objective-C中的常见集合类,如NSString,NSArray,NSDictionary等有一个可变的以及不可变的版本。 分别定义它们的逻辑是什么? 性能,内存pipe理还是其他?

因为不可变对象本身就是一个特定状态唯一标识符,所以这些类的不可变版本是存在的。 也就是说,如果您拥有100个NSString实例的NSArray可以将NSArray实例视为这些string中的任何一个的幂等。

同样,不变性意味着国家出售之后不会发生变化。 例如, NSViewsubviews方法返回一个不可变的数组,从而确保调用者不打算与内容玩游戏(甚至不期望能够)。 在内部, NSView 可以select返回[可能的]包含子视图的NSMutableArray(因为它是内部可变的),而对NSArray的types转换意味着调用者不能在没有恶意强制转换或不良编译器警告的情况下操作内容。 (这可能或可能不是真正的实现,顺便说一句 – 但这种模式在其他地方使用)。

不变性也意味着枚举和/或遍历可以在没有中间状态改变风险的情况下完成。 同样,许多不可变的类也是显式线程安全的; 任何数量的线程都可以同时读取不可变的状态,通常不需要locking。

通常对于一个API来说,一个不可变的类将是线程安全的,所以你可以直接在后台线程中读取它,而不用担心内容会改变。

对于内容可能转移的集合等事情来说,这更重要,您可能正在列举它们。

为什么Objective C中的常见集合类像NSString,NSArray,NSDictionary等有一个可变的以及不可变的版本。

这个概念被用于相关的语言。 值得注意的区别是objctypes被命名为mutable变体。 类似的语言通常通过应用关键字(如const )来实现。 当然,其他相关语言也使用明确可变或不可变的types。

分别定义它们的逻辑是什么?

objc消息不区分const和non-const,并且语言不提供内置支持来确保对象的状态不会改变(尽pipe如果真的倾向于扩展一个编译器,它确实不会是一个难题)。 所以这里也涉及到一点历史。

因此,class级定义不变性与可变性是常规的,并提供区分可变性的变体。

多个抽象也引入了types安全和意图。

性能

是。 有一个不变的对象可以做的多重优化。

有些情况包括:

  • 它可以caching值,永远不会多次计算它们。
  • 它可以使用精确的,不可分配的数据分配。
  • 它通常不需要任何multithreading使用的特殊代码 – 许多不可变types只会在构造/销毁过程中发生变异。
  • 这些types通常使用类集群。 如果实现了一个类集群,那么专门的实现可以在内存,实现等方面提供显着的差异。考虑一个具有一个字符的不可变string如何与一个可变types不同。

内存pipe理

有些情况包括:

  • 不可变的types可以共享它们不变的内容。
  • 他们也可能会减less拨款。 例如, copyWithZone:initWithType:可以返回保留的源,而不是深度或浅的物理副本。

还是其他什么?

这使得编写清晰的界面变得更加容易。 你可以保证和(试图)限制一些东西。 如果一切都是可变的,就会有更多的错误和特例。 实际上,这种区别完全可以改变我们写作objc图书馆的方式:

 [[textField stringValue] appendString:@" Hz"]; [textField stateDidChange]; 

所以很高兴能够传递对象而不用担心客户端会改变背后,而避免复制到所有的地方。

基本上,当你知道一个数据结构是不可变的,你可以做很多优化。 例如,如果一个数组是不可变的,那么当你尝试添加一个对象时,你可以省略所有能“增长”数组的代码,并且你可以简单地把你的不可变数组作为一个id[]的包装。

除了上面提到的答案,一个区别是:不可变对象通常是线程安全的。 而可变对象不是线程安全的。

谢谢