copy和mutableCopy如何应用于NSArray和NSMutableArray?

时间:2010-01-04 20:56:34

标签: cocoa cocoa-touch nsmutablearray nsarray nscopying

copymutableCopy上使用NSArrayNSMutableArray时有什么区别?

这是我的理解;这是对的吗?

// ** NSArray **
NSArray *myArray_imu = [NSArray  arrayWithObjects:@"abc", @"def", nil];

// No copy, increments retain count, result is immutable
NSArray *myArray_imuCopy = [myArray_imu copy];

// Copys object, result is mutable 
NSArray *myArray_imuMuta = [myArray_imu mutableCopy];

// Both must be released later

// ** NSMutableArray **
NSMutableArray *myArray_mut = [NSMutableArray arrayWithObjects:@"A", @"B", nil];

// Copys object, result is immutable
NSMutableArray *myArray_mutCopy = [myArray_mut copy];

// Copys object, result is mutable
NSMutableArray *myArray_mutMuta = [myArray_mut mutableCopy];

// Both must be released later

8 个答案:

答案 0 :(得分:72)

copymutableCopy在不同的协议中定义(分别为NSCopyingNSMutableCopying),NSArray符合两者。 mutableCopy的定义NSArray(不仅仅是NSMutableArray),并且允许您创建最初不可变数组的可变副本:

// create an immutable array
NSArray *arr = [NSArray arrayWithObjects: @"one", @"two", @"three", nil ];

// create a mutable copy, and mutate it
NSMutableArray *mut = [arr mutableCopy];
[mut removeObject: @"one"];

摘要:

  • 无论原始类型如何,您都可以依赖mutableCopy的结果变得可变。对于数组,结果应为NSMutableArray
  • 不能依赖于copy的结果是可变的! copy NSMutableArray {/ 1}可以返回NSMutableArray,因为这是原始类,但copy任意NSArray个实例都会copy

编辑:根据Mark Bessey的回答重新阅读原始代码。当您创建阵列的副本时,无论您对副本执行什么操作,当然您仍然可以修改原件。 mutableCopy vs NSMutableArray -copy会影响新数组是否可变。

修改2:修正了NSMutableArray将返回{{1}}的假设(假设)。

答案 1 :(得分:8)

我认为你必须误解了copy和mutableCopy的工作原理。在第一个示例中,myArray_COPY是myArray的不可变副本。制作副本后,您可以操作原始myArray的内容,而不会影响myArray_COPY的内容。

在第二个示例中,您创建了myArray的可变副本,这意味着您可以修改数组的任一副本,而不会影响另一个副本。

如果我更改第一个示例以尝试从myArray_COPY插入/删除对象,它就会失败,就像您期望的那样。


或许考虑一个典型的用例会有所帮助。通常情况下,您可能会编写一个采用NSArray *参数的方法,并且基本上将其存储以供以后使用。你可以这样做:

- (void) doStuffLaterWith: (NSArray *) objects {
  myObjects=[objects retain];
}

...但是你遇到的问题是可以使用NSMutableArray作为参数调用该方法。创建数组的代码可以在调用doStuffLaterWith:方法之间以及稍后需要使用该值时对其进行操作。在多线程应用程序中,当你迭代它时,甚至可以更改的数组内容,这可能会导致一些有趣的错误。

如果您改为这样做:

- (void) doStuffLaterWith: (NSArray *) objects {
  myObjects=[objects copy];
}

..然后副本会在调用方法时创建数组内容的快照。

答案 2 :(得分:5)

“copy”方法返回通过实现NSCopying协议copyWithZone创建的对象:

如果您向NSString发送一条复制邮件:

NSString* myString;

NSString* newString = [myString copy];

返回值将是NSString(不可变)


mutableCopy方法返回通过实现NSMutableCopying协议的mutableCopyWithZone创建的对象:

发送:

NSString* myString;

NSMutableString* newString = [myString mutableCopy];

返回值 WILL 是可变的。


在所有情况下,对象必须实现协议,表示它将创建新的复制对象并将其返回给您。


在NSArray的情况下,浅层和深层复制存在额外的复杂程度。

NSArray的浅表副本只会复制对原始数组对象的引用,并将它们放入新数组中。

结果是:

NSArray* myArray;

NSMutableArray* anotherArray = [myArray mutableCopy];

[[anotherArray objectAtIndex:0] doSomething];

还会影响原始数组中索引0处的对象。


深层副本实际上会复制数组中包含的各个对象。这是通过向每个单独的对象发送“copyWithZone:”消息来完成的。

NSArray* myArray;

NSMutableArray* anotherArray = [[NSMutableArray alloc] initWithArray:myArray
                                                       copyItems:YES];

编辑删除我对可变对象复制的错误假设

答案 3 :(得分:5)

NSMutableArray* anotherArray = [[NSMutableArray alloc] initWithArray:oldArray
                                                           copyItems:YES];

将创建anotherArray,这是oldArray到2级深度的副本。如果oldArray的对象是数组。在大多数应用中通常都是这种情况。

如果我们需要 True Deep Copy ,我们可以使用,

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
    [NSKeyedArchiver archivedDataWithRootObject: oldArray]];

这将确保实际复制所有级别,保留每个级别的原始对象的可变性。

Robert Clarence D'Almeida, 印度班加罗尔。

答案 4 :(得分:3)

您在原始数组上调用addObject和removeObjectAtIndex,而不是您创建的新副本。调用copy vs mutableCopy只会影响对象的新副本的可变性,而不会影响原始对象。

答案 5 :(得分:2)

简单说明,

  • copy返回数组的不可变(无法修改)副本,
  • mutableCopy返回数组的可变(可修改)副本。

复制(在这两种情况下)意味着你得到一个新阵列"填充"对象引用原始数组(即副本中引用相同(原始)对象。

如果向mutableCopy添加新对象,则它们对mutableCopy是唯一的。如果从mutableCopy中删除对象,则会从原始数组中删除它们。

在两种情况下都考虑副本,作为创建副本时原始数组的时间快照。

答案 6 :(得分:0)

假设

NSArray *A = xxx; // A with three NSDictionary objects
NSMutableArray *B = [A mutableCopy]; 

B的内容是NSDictionary对象而不是NSMutableDictionary,是不是?

答案 7 :(得分:0)

-(id)copy always returns a immutable one & -(id)mutableCopy always returns a mutable object,that's it.

你必须知道这些复制内容的返回类型,并且在声明新对象时,返回值必须是不可变的或可变的,否则编译器会显示错误。

已复制的对象无法使用新对象进行修改,现在它们完全是两个不同的对象。