使用原始元素的可变副本复制 NSArray
我正在类中创建一系列字典。 我想将该数组的副本返回给任何其他需要它的对象。 需要修改传递给其他对象的此副本而不修改原始对象。
因此,我在保存“master”数组的类的 getter 方法中使用以下内容:
[[NSMutableArray alloc] initWithArray:masterArray copyItems:YES];
但是,这似乎使内部的所有字典都是不可变的。 我怎样才能避免这种情况?
我想我在这里遗漏了一些东西。 任何帮助都感激不尽!
I am creating an array of dictionaries in a class. I want to return a copy of that array to any other object that asks for it. This copy that is passed to other objects needs to be modified without modifying the original.
So I am using the following in a getter method of my class that holds the "master" array:
[[NSMutableArray alloc] initWithArray:masterArray copyItems:YES];
However, this seems to make all the dictionaries inside immutable. How can I avoid this?
I think I am missing something here. Any help will be much appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
发布评论
评论(4)
一种方法是滥用键值编码,将 mutableCopy
发送到每个字典,并将 autorelease
发送到每个副本。 但这是非常非常肮脏的黑客行为,所以不要这样做。 事实上,您可能一开始就不应该这样做。
一般来说,当我看到“字典数组”这个词时,我知道你正在使用字典来代替模型对象。 不要那样做。 编写自己的模型类; 当您拥有自己的自定义属性和行为方法时,一切都会变得更加容易。 (有些事情比其他事情更重要:如果没有适当的模型层,实现 AppleScript 支持实际上是不可能的。)
一旦有了真正的模型对象,您就可以在其中实现 NSCopying
而无需担心可变与不可变,因为无论如何你的真实模型类中可能不会有可变性区别。 (我不了解其他人,但我从未在我的模型类中做出过这样的区分。)然后您可以使用现有的 NSArray initWithArray:copyItems:
方法。
吉姆关于 -initWithArray:copyItems
发送 -copyWithZone:
消息到每个元素。 要获取数组元素的可变副本,您需要发送 -mutableCopyWithZone:
(或者只是 -mutableCopy
的简洁)到每个元素。 这相当简单:
NSMutableArray *masterArray = ...
NSMutableArray *clone = [NSMutableArray arrayWithCapacity:[masterArray count]];
for (id anObject in masterArray)
[clone addObject:[anObject mutableCopy]]; // OR [clone addObject:anObject];
但是,您对问题的解释中隐藏着一个更深层次的问题:您似乎希望数组及其元素(字典)都是可变的,但有一些要点需要澄清,特别是考虑到您的规定“传递给其他对象的[]副本需要在不修改原始对象的情况下进行修改。”根据您的具体含义,这可能很难保证并且实施。
例如,假设原始数组包含多个可变字典。 创建前两个级别的可变副本意味着获得副本的人可以修改自己的数组和数组中的字典,而无需直接更改原始数组或字典。 但是,如果字典包含可变对象(例如 NSMutableArray、NSMutableString 等),则使用“副本”的代码可以仅使用对可变副本的引用来间接修改字典的内容。
可变副本是“浅”的,这意味着仅复制第一层,并且副本具有指向与原始结构相同的元素的指针。 因此,保证原件和副本之间没有联系(至少手动)需要扫描整个结构并制作副本。 如果某些元素不符合 NSCopying 或 NSMutableCopying,这可能会变得更加复杂。
最简单、最快的解决方案是使用上面的简单代码或仅返回对主数组的引用。 这需要相信客户端代码不会修改数组,因此这可能不适用于所有情况,但如果您也控制调用代码,这可能会更好。 另一方面,如果您确实想要一个完全独立的副本,请考虑使用 NSCoding / 键控归档:
NSMutableArray *masterArray = ...
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:masterArray];
NSMutableArray *clone = [NSKeyedUnarchiver unarchiveObjectWithData:data];
基本上,这会将所有内容转换为原始数据字节,然后将其重新构成一组新的对象。 为此,字典中的所有对象都必须符合 NSCoding 协议。 这可能需要一些工作,但它是保证对象唯一性的一种优雅的通用方法。 这当然具有非零的性能成本,但如果您绝对必须保证不会有副作用,那么它应该有效。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
您可以采取的另一种方法是使用 CFPropertyListCreateDeepCopy() 函数(在 CoreFoundation 框架中),传入 kCFPropertyListMutableContainers 作为 mutabilityOption 参数。 代码如下所示:
这不仅会创建字典的可变副本,而且还会递归地创建这些字典包含的任何内容的可变副本。 但请注意,只有当您的字典数组仅包含有效属性列表的对象(数组、数字、日期、数据、字符串和字典)时,这才有效,因此这可能适用也可能不适用于您的特定情况。
Another approach you could take is to use the CFPropertyListCreateDeepCopy() function (in the CoreFoundation framework), passing in kCFPropertyListMutableContainers for the mutabilityOption argument. The code would look like:
This will not only create mutable copies of the dictionaries, but it would also make mutable copies of anything contained by those dictionaries recursively. Do note though that this will only work if your array of dictionaries only contains objects that are valid property lists (array, number, date, data, string, and dictionary), so this may or may not be applicable in your particular situation.