Правильный способ скопировать NSMutableArray только для чтения

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

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


person Jon Hull    schedule 06.06.2010    source источник


Ответы (1)


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

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

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

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

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

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

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

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

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

person Peter Hosey    schedule 06.06.2010