我在Cocoa and 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
你不能依赖复制的结果是可变的!复制
NSMutableArray
可能 返回一个NSMutableArray
,因为那是原始类,但复制任意一个NSArray
实例不会。
这似乎与NSURLRequest
有些隔离,因为NSArray
按预期行事:
NSArray *arr = [[NSMutableArray alloc] init];
NSLog( @"%@", [arr className] ); // __NSArrayM
NSLog( @"%@", [[arr copy] className] ); // __NSAraryI
NSLog( @"%@", [[array mutableCopy] className] ); // __NSArrayM
因此...
-copy
何时返回不可变对象(如预期的那样)以及何时返回可变对象?答案 0 :(得分:13)
我认为你发现了文档与现实之间的巨大分歧。
NSCopying协议文档声明:
如果考虑“immutable vs. mutable”适用于接收对象,则返回的副本是不可变的;否则副本的确切性质由班级决定。
但在某些情况下,这显然是错误的,正如您在示例中所示(我已经通过该文档页面向他们发送了反馈)。
但(#2)在我看来,它实际上并不重要,你不应该在乎。
-copy
的要点是它将返回一个可以使用的对象,并保证它将独立于原始对象。这意味着如果你有一个可变对象,{ {1}}它,并且更改原始对象,副本将看不到效果。 (在某些情况下,我认为这意味着-copy
可以被优化为什么都不做,因为如果对象是不可变的,它首先不能改变。我可能错了。(我是现在想知道字典键的含义是什么,但这是一个单独的主题......))
正如您所见,在某些情况下,新对象实际上可能是一个可变类(即使文档告诉我们它不会)。但只要你不依赖它是可变的(你为什么会这样?),这没关系。
你应该怎么做? 始终将-copy
的结果视为不可变,简单如此。
答案 1 :(得分:3)
1)-copy何时返回一个不可变对象(如预期的那样)以及何时返回一个可变对象?
你应该始终将其视为不可变的变体。不应使用返回类型的可变接口。除了优化之外,答案无关紧要,除非记录在案,否则应视为实施细节。
显而易见的情况:由于多种原因,objc类集群和类设计可能很复杂。返回一个可变副本可能只是为了方便。
2)如何获得拒绝被“冻结”的可变对象的“冻结”副本的预期效果?
使用不可变类的复制构造函数是一种好方法(类似于St3fan的答案)。像copy
一样,这不是保证。
我能想到你为什么要强制执行这种行为的唯一原因是为了表现或强制执行受限制的界面(除非它是学术性的)。如果您需要性能或受限制的接口,那么您可以简单地封装在创建时复制的类型的实例,并仅公开不可变接口。然后通过retain实现复制(如果这是你的意图)。
或者,您可以编写自己的子类并实现自己的副本变体。
最后的手段:许多cocoa mutable / immutable类都是纯粹的接口 - 如果你需要确保特定的行为,你可以编写自己的子类 - 但这很不寻常。
或许可以更好地描述为什么应该强制执行此功能 - 现有的实现对绝大多数开发人员/用户来说都很好。
答案 2 :(得分:1)
请记住,没有一个copy
实现 - 每个类都实现自己的实现。并且,众所周知,Objective C运行时的实现有点“松散的goosey”。所以我认为我们可以说主要是 copy
返回一个不可变版本,但是存在一些例外。
(顺便说一句,这是做什么的:
NSArray *arr = [[NSMutable array] init];
?)
答案 3 :(得分:-1)
将对象转换为可变对象的最佳方法是使用可变的“构造函数”。例如:
NSArray* array = ...;
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray: array];
复制用于制作对象的副本。不要改变它的可变性。