9

我在Cocoa 和 Objective C: Up and Running中读到它-copy总是返回一个不可变对象并且-mutableCopy总是返回一个可变对象:

重要的是要知道调用-copy可变对象会返回不可变版本。如果要在新版本中复制可变对象并保持可变性,则必须调用-mutableCopy原始对象。不过,这很有用,因为如果你想“冻结”一个可变对象,你可以调用-copy它。

所以我有这样的事情:

NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init];
NSLog( @"%@", [req className] );               // NSMutableURLRequest
NSLog( @"%@", [[req copy] className] );        // NSMutableURLRequest
NSLog( @"%@", [[req mutableCopy] className] ); // NSMutableURLRequest

根据这个先前的答案

您不能依赖复制的结果是可变的!复制 anNSMutableArray可能会返回 an NSMutableArray,因为那是原始类,但复制任意 NSArray实例不会。

这似乎有点孤立于NSURLRequest,因为NSArray按预期行事:

NSArray *arr = [[NSMutableArray alloc] init];
NSLog( @"%@", [arr className] );                 // __NSArrayM
NSLog( @"%@", [[arr copy] className] );          // __NSAraryI
NSLog( @"%@", [[array mutableCopy] className] ); // __NSArrayM

所以...

  1. 什么时候-copy返回一个不可变对象(如预期的那样),什么时候返回一个可变对象?
  2. 如何获得拒绝“冻结”的可变对象的“冻结”副本的预期效果?
4

4 回答 4

13

我认为您已经发现了文档和现实之间的巨大裂痕。

NSCopying 协议文档声称:

如果“不可变与可变”的考虑适用于接收对象,则返回的副本是不可变的;否则副本的确切性质由班级确定。

但这在某些情况下显然是错误的,正如您在示例中所展示的那样(我已通过该文档页面向他们发送了有关此问题的反馈)。

但是(#2)在我看来,这实际上并不重要,你不应该关心。

关键-copy是它将返回一个您可以使用的对象,并保证它的行为独立于原始对象。这意味着如果你有一个可变对象,-copy它,并且改变了原始对象,副本将看不到效果。(在某些情况下,我认为这意味着-copy可以优化为什么都不做,因为如果对象是不可变的,那么它一开始就无法更改。我可能错了。(我现在想知道这意味着什么正因为如此,用于字典键,但这是一个单独的主题......))

如您所见,在某些情况下,新对象实际上可能是一个可变类(即使文档告诉我们它不会)。但只要你不依赖它是可变的(你为什么会这样?),这并不重要。

你该怎么办?总是把结果-copy当作不可变的,就这么简单。

于 2011-08-27T04:00:57.993 回答
3

1) -copy 什么时候返回一个不可变对象(如预期的那样),什么时候返回一个可变对象?

您应该始终将其视为不可变的变体。不应使用返回类型的可变接口。除了优化之外,答案应该无关紧要,除非记录在案,否则应将其视为实现细节。

显而易见的情况:由于多种原因,objc 类集群和类设计可能很复杂。返回可变副本可能只是为了方便。

2)如何实现获得拒绝“冻结”的可变对象的“冻结”副本的预期效果?

使用不可变类的复制构造函数是一种好方法(类似于 St3fan 的答案)。像copy,这不是保证。

我能想到你为什么要强制执行此行为的唯一原因是为了性能或强制执行受限接口(除非它是学术性的)。如果您想要性能或受限制的接口,那么您可以简单地封装一个在创建时复制并仅公开不可变接口的类型的实例。然后您通过保留实现复制(如果这是您的意图)。

或者,您可以编写自己的子类并实现自己的副本变体。

最后的手段:许多可可可变/不可变类都是纯粹的接口——如果你需要确保特定的行为,你可以编写自己的子类——但这很不寻常。

perhaps a better description of why this should be enforced would be good - the existing implementations work just fine for the vast majority of developers/uses.

于 2011-08-27T04:18:43.713 回答
1

请记住,没有一个copy实现——每个类都有自己的实现。而且,众所周知,Objective C 运行时的实现在某些地方有点“松懈”。所以我认为我们可以说它主要 copy返回一个不可变的版本,但存在一些例外。

(顺便说一句,这是做什么的:

NSArray *arr = [[NSMutable array] init];

?)

于 2011-08-27T03:03:37.883 回答
-1

将对象转换为可变对象的最佳方法是使用可变的“构造函数”。例如:

NSArray* array = ...;
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray: array];

Copy 用于制作对象的副本。不改变它的可变性。

于 2011-08-27T03:53:18.240 回答