正确的方法来复制只读NSMutableArray

时间:2010-06-06 09:38:03

标签: iphone cocoa nsmutablearray readonly nscopying

我有一个readonly属性的对象,我正在尝试实现NSCopying。它有一个名为“subConditions”的mutableArray(它包含“SubCondition”对象)。我已经将它设为readonly因为我希望调用者能够更改数组中的数据,但不能更改数组本身。这非常有效,直到编写-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;
}

这是复制readonly mutableArray的正确/最佳方法吗?

1 个答案:

答案 0 :(得分:2)

  

我已经[可变数组属性] readonly,因为我希望调用者能够更改数组中的数据,但不能更改数组本身。

在不知情的情况下改变对象属性的值是坏事。例如,假设对象具有一个索引集,通过该索引集可以知道选择了哪些对象;如果另一个对象然后从数组中删除了一些子条件,则集合中的部分或全部索引可能会引用错误的对象或根本没有对象,这意味着访问所选的子条件会导致NSOutOfRangeException。

这是一个坏习惯,解决方案是转向逆习惯,这是永远不会这样做的。始终通过告知其所有者进行更改来更改所拥有的数组 - 或者至少使用所应用的更改创建自己的数组,并将其显示给原始数组的所有者。在后一个解决方案中,所有者(条件)可能会清除其索引集并忘记选择,但这仍然比导致异常更好。

将属性的类型从NSMutableArray更改为NSArray后,上面的代码应该发出警告,因为只有NSMutableArrays响应setArray:。 (代码仍然工作,假设subConditions访问器返回可变数组而不是自动释放的副本,但编译器会发出警告。)将数组从表中变更,问题变成了如何使Condition类提供一个Condition实例的副本与复制的子条件的复制数组,而不允许其他类这样做。

一种解决方案是将新数组直接存储到副本的实例变量中。通常情况下,这也是错误的mojo,但在这个特定的上下文中(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对您的应用程序进行概要分析,以确定开销是否对您关心的硬件很重要。