我有一个具有只读属性的对象,我正在尝试为其实现 NSCopying。它有一个名为“subConditions”的可变数组(其中保存“SubCondition”对象)。我将其设为只读,因为我希望调用者能够更改数组中的数据,但不能更改数组本身。在编写 -copyWithZone: 方法之前,这种方法非常有效。
经过一番摸索后,我终于找到了一些似乎可行的东西。我不确定这是否是最佳实践。这是我的 -copyWithZone: 方法的简化版本:
-(id)copyWithZone:(NSZone*)zone
{
Condition *copy = [[[self class]allocWithZone:zone]init];
NSArray *copiedArray = [[NSArray alloc]initWithArray:self.subConditions copyItems:YES];
[copy.subConditions setArray:copiedArray];
[copiedArray release];
return copy;
}
这是复制只读可变数组的正确/最佳方法吗?
最佳答案
I have made [the mutable array property] readonly because I want callers to be able to change the data in the array, but not the array itself.
在对象不知情的情况下改变对象属性的值是不好的魔力。举个例子,假设对象有一个索引集,通过该索引集它知道选择了哪些对象;如果另一个对象从数组中删除一些子条件,则集合中的部分或全部索引可能引用错误的对象或根本没有对象,这意味着访问选定的子条件将导致 NSOutOfRangeException。
这是一个坏习惯,解决方案是改成相反的习惯,即永远不要这样做。始终通过告诉其所有者进行更改来对拥有的数组进行更改,或者至少使用应用的更改创建您自己的数组并将其显示给原始数组的所有者。在后一种解决方案中,所有者(条件)可能会删除其索引集并忘记选择,但这仍然比引发异常要好。
一旦将属性的类型从 NSMutableArray 更改为 NSArray,上面的代码应该会发出警告,因为只有 NSMutableArrays 才会响应 setArray:
。 (假设 subConditions
访问器返回可变数组而不是自动释放的副本,代码仍然可以工作,但编译器会给出有关它的警告。)离开表格后,问题就变成了如何使 Condition 类能够提供 Condition 实例的副本以及复制的子条件的复制数组,同时不允许其他类执行此操作。
一种解决方案是简单地将新数组直接存储到副本的实例变量中。通常,这也是不好的魔力,但在这个特定的上下文中(copyWithZone:
,受影响的对象是副本),这是极少数适合的情况之一。使用指向成员的指针运算符来执行此操作:
copy->subConditions = [[NSMutableArray alloc]initWithArray:self.subConditions copyItems:YES];
另一个解决方案是使用 class extension在类的实现文件中将属性重新声明为readwrite
。 (将 readonly
声明保留在类的头文件中。)然后您可以使用属性访问消息:
copy.subConditions = [[[NSArray alloc]initWithArray:self.subConditions copyItems:YES]autorelease];
没有什么值得推荐的,只是直接 ivar 访问稍微快一些:属性版本创建一个临时数组,并发送一条访问器消息以使新条件创建该数组的副本。您可能希望首先使用属性访问版本,然后使用 Instruments 分析您的应用程序,以确定开销对您关心的硬件是否重要。
关于iphone - 复制只读 NSMutableArray 的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2983729/