Я сделал [свойство изменяемого массива] доступным только для чтения, потому что я хочу, чтобы вызывающие абоненты могли изменять данные в массиве, но не сам массив.
Мутация значения свойства объекта без его знания - плохое умение. В качестве примера предположим, что у объекта есть индекс, по которому он знает, какие объекты выбраны; если другой объект затем удаляет некоторые подусловия из массива, некоторые или все индексы в наборе могут ссылаться на неправильные объекты или вообще не использовать объекты, что означает, что доступ к выбранным подусловиям вызовет исключение 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, чтобы определить, влияют ли накладные расходы на оборудование, которое вас волнует.