使用原始元素的可变副本复制 NSArray

发布于 07-28 22:10 字数 297 浏览 13 评论 0原文

我正在类中创建一系列字典。 我想将该数组的副本返回给任何其他需要它的对象。 需要修改传递给其他对象的此副本而不修改原始对象。

因此,我在保存“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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梅倚清风2024-08-04 22:10:28

您可以采取的另一种方法是使用 CFPropertyListCreateDeepCopy() 函数(在 CoreFoundation 框架中),传入 kCFPropertyListMutableContainers 作为 mutabilityOption 参数。 代码如下所示:

NSMutableArray* originalArray;
NSMutableArray* newArray;

newArray = (NSMutableArray*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFPropertyListRef)originalArray, kCFPropertyListMutableContainers);

这不仅会创建字典的可变副本,而且还会递归地创建这些字典包含的任何内容的可变副本。 但请注意,只有当您的字典数组仅包含有效属性列表的对象(数组、数字、日期、数据、字符串和字典)时,这才有效,因此这可能适用也可能不适用于您的特定情况。

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:

NSMutableArray* originalArray;
NSMutableArray* newArray;

newArray = (NSMutableArray*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFPropertyListRef)originalArray, kCFPropertyListMutableContainers);

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.

橙幽之幻2024-08-04 22:10:28

复制数组将创建数组对象的副本,并引用原始内容对象(适当保留)。

根据文档,-initWithArray:copyItems:使用copies<初始化新数组/strong> 原始数组中的项目。 该副本是通过发送原始内容对象-copyWithZone:来创建的,这将在对象可变的情况下创建不可变的副本。

如果您需要不同的行为(即内容对象的可变副本或内容对象的深层副本),您将必须编写自己的便利函数/方法来执行此操作。

Copying an array will create a copy of the array object, with references to the original content objects (appropriately retained.)

Per the documentation, -initWithArray:copyItems: initializes the new array with copies of the items in the original array. This copy is created by sending the original content objects -copyWithZone:, which will create an immutable copy in the case of mutable objects.

If you need different behavior (i.e. mutable copies of the content objects, or deep copies of the content objects) you'll have to write your own convenience function/method to do so.

稀香2024-08-04 22:10:28

一种方法是滥用键值编码,将 mutableCopy 发送到每个字典,并将 autorelease 发送到每个副本。 但这是非常非常肮脏的黑客行为,所以不要这样做。 事实上,您可能一开始就不应该这样做。

一般来说,当我看到“字典数组”这个词时,我知道你正在使用字典来代替模型对象。 不要那样做。 编写自己的模型类; 当您拥有自己的自定义属性和行为方法时,一切都会变得更加容易。 (有些事情比其他事情更重要:如果没有适当的模型层,实现 AppleScript 支持实际上是不可能的。)

一旦有了真正的模型对象,您就可以在其中实现 NSCopying 而无需担心可变与不可变,因为无论如何你的真实模型类中可能不会有可变性区别。 (我不了解其他人,但我从未在我的模型类中做出过这样的区分。)然后您可以使用现有的 NSArray initWithArray:copyItems: 方法。

One way would be to abuse Key-Value Coding to send mutableCopy to each of the dictionaries and autorelease to each of the copies. But that's a dirty, dirty hack, so don't do that. Indeed, you probably shouldn't be doing this in the first place.

Generally, when I see the words “array of dictionaries”, I get the idea that you're using dictionaries as substitutes for model objects. Don't do that. Write your own model classes; everything becomes much easier when you have your own custom properties and behavior methods to work with. (Some things more than others: Implementing AppleScript support is virtually impossible without a proper model layer.)

And once you have real model objects, you can implement NSCopying in them and not need to worry about mutable versus immutable, since you probably won't have a mutability distinction in your real model classes anyway. (I don't know about others, but I've never made such a distinction in my model classes.) Then you can just use the existing NSArray initWithArray:copyItems: method.

み青杉依旧2024-08-04 22:10:28

吉姆关于 -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 协议。 这可能需要一些工作,但它是保证对象唯一性的一种优雅的通用方法。 这当然具有非零的性能成本,但如果您绝对必须保证不会有副作用,那么它应该有效。

Jim is correct about -initWithArray:copyItems sending a -copyWithZone: message to each element. To get mutable copies of array elements, you'd need to send -mutableCopyWithZone: (or just -mutableCopy for brevity) to each element. This is fairly straightforward:

NSMutableArray *masterArray = ...
NSMutableArray *clone = [NSMutableArray arrayWithCapacity:[masterArray count]];
for (id anObject in masterArray)
    [clone addObject:[anObject mutableCopy]]; // OR [clone addObject:anObject];

However, there is a deeper question hidden in your explanation of the problem: it seems you want both the array and its elements (dictionaries) to be mutable, but there are a few fine points that should be cleared up, especially in light of your stipulation that "[the] copy that is passed to other objects needs to be modified without modifying the original." Depending on exactly what you mean, this can be quite complex to guarantee and implement.

For example, suppose that the original array contains a number of mutable dictionaries. Creating mutable copies of these first two levels means that someone who obtains a copy can modify their own array and dictionaries in the array without changing the original array or dictionary directly. However, if a dictionary contains mutable objects (like an NSMutableArray, NSMutableString, etc.), the code using the "copy" could modify the contents of the dictionary indirectly, using only a reference to the mutable copy.

Mutable copies are "shallow", meaning only the first level is copied, and the copy has pointers to the same elements as the original structure. Thus, guaranteeing that there is no linkage between the original and the copy (at least, manually) would require a sweep through the entire structure and making copies. This can become more complicated if some elements don't conform to NSCopying or NSMutableCopying.

The easiest and fastest solution is to use the simple code above or just return a reference to the master array. This requires trusting that the client code will not modify the array, so this may not work in all situations, but if you control the calling code as well, this may be preferable. On the other hand, if you definitely want a totally separate copy, consider making use of NSCoding / keyed archiving:

NSMutableArray *masterArray = ...
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:masterArray];
NSMutableArray *clone = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Basically, this converts everything into raw data bytes, then reconstitutes it into a new set of objects. For this to work, all the objects in the dictionary must conform to the NSCoding protocol. This can take a bit of doing, but it's an elegant generic way to guarantee object uniqueness. This certainly has a non-zero performance cost, but if you absolutely must guarantee there will be no side effects, it should work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文