В чем разница между copy и mutableCopy при использовании на NSArray или NSMutableArray?

Это мое понимание; это правильно?

// ** 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
66
fuzzygoat 4 Янв 2010 в 23:56
1
У вас есть ошибка при редактировании; не уверен, опечатка это или недоразумение. В первом блоке кода переменная myArray_imuMuta, присвоенная из mutableCopy, является mutable , а не неизменной, как указывает ваш комментарий.
 – 
Asher Dunn
6 Янв 2010 в 22:29
Спасибо, это была ошибка, я запутался с сообщением Xcode: «Предупреждение NSArray может не реагировать на add -addObject / -removeObjectAtIndex. Я заменю myArray_imuMuta на NSMutableArray.
 – 
fuzzygoat
7 Янв 2010 в 01:01
- пожалуйста, прекратите редактирование во имя «совместимости с ARC» - см. этот мета-пост, чтобы узнать, почему.
 – 
Krease
6 Дек 2014 в 10:12

8 ответов

Лучший ответ

copy и mutableCopy определены в разных протоколах (NSCopying и NSMutableCopying соответственно), а 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 может вернуть NSMutableArray, поскольку это исходный класс, но copy любой произвольный экземпляр NSArray будет нет.

Изменить: перечитайте исходный код в свете ответа Марка Бесси. Когда вы создаете копию своего массива, конечно, вы все равно можете изменить оригинал независимо от того, что вы делаете с копией. copy vs mutableCopy влияет на то, является ли новый массив изменяемым.

Редактировать 2: исправлено мое (ложное) предположение, что NSMutableArray -copy вернет NSMutableArray.

73
Asher Dunn 6 Янв 2010 в 22:26
4
Отправка -copy в NSMutableArray обычно не возвращает другой изменяемый массив. Он, конечно, мог бы сделать это , но в обычном случае он возвращает неизменяемый массив с постоянным доступом.
 – 
Mark Bessey
5 Янв 2010 в 00:17
Ой, ты прав. Я неправильно прочитал код OP и подумал, что он изменяет результат -copy. Для NSMutableArray -copy было бы столь же разумно возвращать другой изменяемый массив, поэтому я (ошибочно) предположил, что это так.
 – 
Asher Dunn
5 Янв 2010 в 00:19
Я что-то упустил, так как не могу найти ссылку на [mut remove: @ "one"]; должно быть не [mut removeObjectIdenticalTo: @ "one"]; Я просто хочу уточнить для тех, кто читает это и пытается учиться?
 – 
fuzzygoat
5 Янв 2010 в 20:10
К сожалению, это должно было быть [mut removeObject: @ "one"] ;. removeObject использует -isEqual для определения совпадений, и две строки считаются равными, если они имеют одинаковое содержимое. removeObjectIdenticalTo удаляет только переданный объект (он сравнивает адреса объектов, а не содержимое). [mut removeObjectIdenticalTo: @ "one"] создает новый временный объект NSString, которого нет в массиве, и поэтому его нельзя удалить из массива.
 – 
Asher Dunn
6 Янв 2010 в 22:26
6
«копирование NSMutableArray может вернуть NSMutableArray» Нет, оно НИКОГДА не возвращает NSMutableArray. См. developer.apple.com/library/mac/documentation/Cocoa/Conceptual/… " Если к объекту применяется концепция «неизменяемый или изменчивый», NSCopying создает неизменяемые копии независимо от того, является ли оригинал неизменным или нет. . "
 – 
user102008
21 Янв 2011 в 02:42

Я думаю, вы, должно быть, неправильно истолковали, как работают 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];
}

... затем копия создает моментальный снимок содержимого массива во время вызова метода.

9
Mark Bessey 5 Янв 2010 в 00:28

Метод «copy» возвращает объект, созданный реализацией протоколов NSCopying copyWithZone:

Если вы отправляете NSString копию сообщения:

NSString* myString;

NSString* newString = [myString copy];

Возвращаемое значение будет NSString (неизменяемым).


Метод mutableCopy возвращает объект, созданный путем реализации mutableCopyWithZone протокола NSMutableCopying:

Отправив:

NSString* myString;

NSMutableString* newString = [myString mutableCopy];

Возвращаемое значение БУДЕТ изменяемым.


Во всех случаях объект должен реализовывать протокол, что означает, что он создаст новый объект-копию и вернет его вам.


В случае с NSArray есть дополнительный уровень сложности, касающийся мелкого и глубокого копирования.

Неглубокая копия NSArray скопирует только ссылки на объекты исходного массива и поместит их в новый массив.

В результате:

NSArray* myArray;

NSMutableArray* anotherArray = [myArray mutableCopy];

[[anotherArray objectAtIndex:0] doSomething];

Также повлияет на объект с индексом 0 в исходном массиве.


Глубокая копия фактически скопирует отдельные объекты, содержащиеся в массиве. Это делается путем отправки каждому отдельному объекту сообщения «copyWithZone:».

NSArray* myArray;

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

Отредактировано, чтобы удалить мое неправильное предположение о копировании изменяемого объекта

5
Corey Floyd 6 Янв 2010 в 09:30
NSMutableArray* anotherArray = [[NSMutableArray alloc] initWithArray:oldArray
                                                           copyItems:YES];

Создаст anotherArray, который является копией oldArray на 2 уровня в глубину. Если объект oldArray является массивом. Как правило, это так в большинстве приложений.

Что ж, если нам нужна True Deep Copy , мы могли бы использовать,

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

Это обеспечит фактическое копирование всех уровней с сохранением изменчивости исходного объекта на каждом уровне.

Роберт Кларенс Д'Алмейда, Бангалор, Индия.

5
robertt almeida 30 Дек 2011 в 20:38
Ваша концепция "True Deep Copy" действительно хороша .. Именно то, что я хочу ... Большое спасибо и +1 за вас .....
 – 
Nirav Gadhiya
13 Авг 2014 в 17:58

Вы вызываете addObject и removeObjectAtIndex в исходном массиве, а не в созданной вами новой копии. Вызов copy vs mutableCopy влияет только на изменчивость новой копии объекта, но не исходного объекта.

3
Dewayne Christensen 5 Янв 2010 в 01:56

Предполагать

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

Содержимое B - это объект NSDictionary, а не NSMutableDictionary, верно?

0
ZYiOS 12 Май 2011 в 08:28
-(id)copy always returns a immutable one & -(id)mutableCopy always returns a mutable object,that's it.

Вы должны знать тип возврата этих копируемых материалов, и при объявлении нового объекта, которому будет назначен объект, возвращаемое значение должно быть неизменным или изменяемым, иначе компилятор покажет вам ошибку.

Скопированный объект не может быть изменен с помощью нового, теперь это два разных объекта.

0
Sauvik Dolui 19 Фев 2014 в 13:13

Проще говоря,

  • copy возвращает неизменяемую (не может быть изменена) копию массива,
  • mutableCopy возвращает изменяемую (можно изменять) копию массива.

Копирование (в обоих случаях) означает, что вы получаете новый массив, "заполненный" ссылками на объекты исходного массива (т.е. в копиях есть ссылки на одни и те же (исходные) объекты.

Если вы добавляете новые объекты в mutableCopy, они уникальны для mutableCopy. Если вы удалите объекты из mutableCopy, они будут удалены из исходного массива.

Думайте о копии в обоих случаях как о моментальном снимке исходного массива во время создания копии.

2
kgdesouz 5 Июл 2014 в 16:21