Правильный способ скопировать только для чтения NSMutableArray - PullRequest
2 голосов
/ 06 июня 2010

У меня есть объект со свойством только для чтения, для которого я пытаюсь реализовать 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;
}

Это правильный / лучший способ скопировать изменяемый массив только для чтения?

1 Ответ

2 голосов
/ 06 июня 2010

Я сделал [свойство изменяемого массива] доступным только для чтения, потому что я хочу, чтобы вызывающие абоненты могли изменять данные в массиве, но не сам массив.

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

Это плохая привычка, и решение состоит в том, чтобы переключиться на обратную привычку, которая никогда не делает этого. Всегда вносите изменения в собственный массив, приказывая его владельцу внести изменения или, по крайней мере, сделайте свой собственный массив с примененными изменениями и покажите это владельцу исходного массива. Владелец (Condition) в этом последнем решении, вероятно, сотрет свой набор индексов и забудет выбор, но это все же лучше, чем вызывать исключения.

Как только вы измените тип свойства с NSMutableArray на NSArray, приведенный выше код должен выдавать предупреждение, поскольку только NSMutableArrays отвечают на setArray:. (Код по-прежнему будет работать , предполагая, что средство доступа subConditions возвращает изменяемый массив, а не автоматически выпущенную копию, но компилятор выдаст предупреждение об этом.) При мутировании массива со таблицы проблема становится, как включить класс Condition для предоставления копии экземпляра Condition с скопированным массивом скопированных подусловий, не позволяя другим классам делать это.

Одно из решений - просто сохранить новый массив непосредственно в переменной экземпляра копии. Как правило, это тоже было бы плохо для mojo, но в данном конкретном контексте (copyWithZone:, с затронутым объектом, являющимся копией), это один из немногих случаев, когда это уместно. Для этого используйте оператор указатель на член:

copy->subConditions = [[NSMutableArray alloc]initWithArray:self.subConditions copyItems:YES];

Другое решение заключается в использовании расширения класса для повторного объявления свойства как readwrite в файле реализации класса. (Сохраните объявление readonly в заголовочном файле класса.) Затем вы можете использовать сообщение о доступе к свойству:

copy.subConditions = [[[NSArray alloc]initWithArray:self.subConditions copyItems:YES]autorelease];

Не рекомендуется много рекомендовать один над другим, за исключением того, что прямой доступ к ivar немного быстрее: версия свойства создает временный массив и отправляет сообщение доступа, чтобы новое условие создало свою копию этого массива. Вы можете сначала использовать версию доступа к свойству, а затем профилировать свое приложение с помощью Instruments, чтобы определить, влияют ли накладные расходы на оборудование, которое вас волнует.

...