复制只读 NSMutableArray 的正确方法
我有一个具有只读属性的对象,我正在尝试为其实现 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;
}
这是复制只读 mutableArray 的正确/最佳方法吗?
I have an object with a readonly property that I am trying to implement NSCopying for. It has a mutableArray called "subConditions" (which holds "SubCondition" objects). I have made it readonly because I want callers to be able to change the data in the array, but not the array itself. This worked really well until it was time to write the -copyWithZone: method.
After fumbling around a bit, I managed to get something that seems to work. I am not sure if it is the best practice though. Here is a simplified version of my -copyWithZone: method:
-(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;
}
Is this the correct/best way to copy a readonly mutableArray?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在对象不知情的情况下改变其属性的值是不好的魔力。举个例子,假设对象有一个索引集,通过该索引集它知道选择了哪些对象;如果另一个对象从数组中删除一些子条件,则集合中的部分或全部索引可能引用错误的对象或根本没有对象,这意味着访问选定的子条件将导致 NSOutOfRangeException。
这是一个坏习惯,解决办法就是改成相反的习惯,也就是永远不要这样做。始终通过告诉其所有者进行更改来对拥有的数组进行更改,或者至少使用应用的更改创建您自己的数组并将其显示给原始数组的所有者。在后一种解决方案中,所有者(条件)可能会清除其索引集并忘记选择,但这仍然比引发异常要好。
一旦将属性的类型从 NSMutableArray 更改为 NSArray,上面的代码应该会发出警告,因为只有 NSMutableArrays 才会响应
setArray:
。 (假设subConditions
访问器返回可变数组而不是自动释放的副本,代码仍然可以工作,但编译器会给出有关它的警告。)离开表格后,问题就变成了如何使 Condition 类能够提供 Condition 实例的副本以及复制的子条件的复制数组,同时不允许其他类执行此操作。一种解决方案是简单地将新数组直接存储到副本的实例变量中。通常,这也是不好的魔力,但在这个特定的上下文中(
copyWithZone:
,受影响的对象是副本),这是极少数适合的情况之一。使用指向成员的指针运算符来执行此操作:另一个解决方案是使用 类扩展,在类的实现文件中将属性重新声明为
readwrite
。 (将readonly
声明保留在类的头文件中。)然后您可以使用属性访问消息:没有太多值得推荐的方法,只是直接 ivar 访问速度稍快一些: version 创建一个临时数组,并发送一条访问器消息以使新条件创建该数组的副本。您可能希望首先使用属性访问版本,然后使用 Instruments 分析您的应用程序,以确定开销对您关心的硬件是否重要。
Mutating the value of an object's property without its knowledge is bad mojo. As an example, suppose that the object has an index set by which it knows which objects are selected; if another object then removes some sub-conditions from the array, some or all of the indexes in the set may refer to wrong objects or no objects at all, which means that accessing the selected sub-conditions would cause an NSOutOfRangeException.
This is a bad habit, and the solution is to switch to the inverse habit, which is to never do this. Always make changes to an owned array by telling its owner to make the changes—or, at least, make your own array with the changes applied and show that to the original array's owner. The owner (Condition) will, in that latter solution, probably wipe out its index set and forget the selection, but that's still better than causing exceptions.
Once you change the property's type from NSMutableArray to NSArray, the above code should give a warning, since only NSMutableArrays respond to
setArray:
. (The code will still work, presuming thesubConditions
accessor returns the mutable array and not an autoreleased copy, but the compiler will give a warning about it.) With mutating the array off the table, the problem becomes how to enable the Condition class to supply a copy of a Condition instance with a copied array of copied sub-Conditions while not allowing other classes to do it.One solution is to simply store the new array directly into the copy's instance variable. Normally, this, too, would be bad mojo, but in this specific context (
copyWithZone:
, with the affected object being the copy), this is one of the very few cases where it is appropriate. Use the pointer-to-member operator to do that:The other solution is to use a class extension to re-declare the property as
readwrite
within the class's implementation file. (Keep thereadonly
declaration in the class's header file.) Then you can use a property access message:There's not much to recommend one over the other, except that the direct ivar access is slightly faster: The property version creates a temporary array, and sends an accessor message to make the new Condition create its copy of that array. You may want to use the property access version first, then profile your application using Instruments to determine whether the overhead matters on hardware you care about.