Объектная переменчивость

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

Почему непостоянные и неизменные объектные варианты?

Объекты по умолчанию являются непостоянными. Большинство объектов позволяет Вам изменять свои инкапсулированные данные через методы доступа метода set. Например, можно изменить размер, расположение, заголовок, буферизовав поведение и другие характеристики NSWindow объект. Хорошо разработанный объект модели — говорит, объект, представляющий запись клиента — требует методов установщика изменить ее данные экземпляра.

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

Несмотря на то, что эти классы имеют нетипичные имена, они ближе к непостоянной норме, чем их неизменные дубликаты. Почему эта сложность? Какая цель делает наличие неизменного варианта непостоянной объектной подачи?

Рассмотрите сценарий, где все объекты способны к тому, чтобы быть видоизмененным. В Вашем приложении Вы вызываете метод и возвращены ссылка на объект, представляющий строку. Вы используете эту строку в своем пользовательском интерфейсе для идентификации определенной части данных. Теперь другая подсистема в Вашем приложении получает свою собственную ссылку на ту же самую строку и решает видоизменить его. Внезапно Ваша метка изменилась из-под Вас. Вещи могут стать еще более страшными, если, например, Вы получаете ссылку на массив, который Вы используете для заполнения табличного представления. Пользователь выбирает строку, соответствующую объекту в массиве, удаленном некоторым кодом в другое место в программе, и проблемы следуют. Неизменность является гарантией, что объект неожиданно не изменится в значении при использовании его.

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

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

Несмотря на то, что в неизменности теории гарантирует, что значение объекта стабильно, на практике эту гарантию не всегда гарантируют. Метод может принять решение раздать непостоянный объект под типом возврата его неизменного варианта; позже, это может решить видоизменить объект, возможно нарушив предположения и выбор, который получатель сделал на основе более раннего значения. Переменчивость самого объекта может измениться, поскольку она подвергается различным трансформациям. Например, сериализируя список свойств (использующий NSPropertyListSerialization класс), не сохраняет аспект переменчивости объектов, только их общий вид — словарь, массив, и т.д. Таким образом при десериализации этого списка свойств полученные объекты не могли бы иметь того же класса как исходные объекты. Например, что было один раз NSMutableDictionary объект мог бы теперь быть a NSDictionary объект.

Программирование с непостоянными объектами

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

Этот раздел исследует серые области в этих инструкциях, обсуждая типичный выбор, который необходимо сделать при программировании с непостоянными объектами. Это также дает обзор методов в платформе Основы для создания непостоянных объектов и для преобразования между непостоянными и неизменными объектными вариантами.

Создание и преобразование непостоянных объектов

Можно создать непостоянный объект через вложенный стандарт выделение-init сообщение — например:

NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] init];

Однако много непостоянных классов предлагают инициализаторы и методы фабрики, позволяющие Вам указать начальную или вероятную способность объекта, такой как arrayWithCapacity: метод класса NSMutableArray:

NSMutableArray *mutArray = [NSMutableArray arrayWithCapacity:[timeZones count]];

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

Можно также создать непостоянный объект путем создания непостоянной копии существующего объекта того общего типа. Для этого вызовите mutableCopy метод, что каждый неизменный суперкласс Основы непостоянные реализации класса:

NSMutableSet *mutSet = [aSet mutableCopy];

В другом направлении можно отправить copy к непостоянному объекту сделать неизменную копию объекта.

Много Фундаментальных классов с неизменными и непостоянными вариантами включают методы для преобразования между вариантами, включая:

  • ввестиWithВвести:— например, arrayWithArray:

  • setВвести:— например, setString: (только непостоянные классы)

  • initWithВвести:copyItems:— например, initWithDictionary:copyItems:

Хранение и возврат непостоянных переменных экземпляра

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

Обычно, когда у Вас есть объект, содержание которого изменяет оптовую торговлю, лучше использовать неизменный объект. Строки (NSString) и объекты данных (NSData) обычно попадайте в эту категорию. Если объект, вероятно, изменится инкрементно, это - разумный подход для создания его непостоянным. Наборы, такие как массивы и словари попадают в эту категорию. Однако частота изменений и размер набора должны быть факторами в этом решении. Например, если у Вас есть маленький массив, редко изменяющийся, лучше сделать его неизменным.

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

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

  • Если значение переменной экземпляра часто изменяется, но Вы редко возвращаете его клиентам в методах получателя, можно сделать переменную экземпляра непостоянной, но возвратить неизменную копию ее в методе доступа; в управляемых памятью программах этот объект был бы автовыпущен (Перечисление 9-1).

Перечисление 9-1  Возвращая неизменную копию непостоянной переменной экземпляра

@interface MyClass : NSObject {
    // ...
    NSMutableSet *widgets;
}
// ...
@end
 
@implementation MyClass
- (NSSet *)widgets {
    return (NSSet *)[[widgets copy] autorelease];
}

Один сложный подход для обработки непостоянных наборов, возвращающихся клиентам, должен поддержать флаг, записывающий, является ли объект в настоящее время непостоянным или неизменным. Если существует изменение, сделайте объект непостоянным и примените изменение. При распространении набора сделайте объект неизменным (если необходимый) прежде, чем возвратить его.

Получение непостоянных объектов

invoker метода интересуется переменчивостью возвращенного объекта по двум причинам:

  • Это хочет знать, может ли это изменить значение объекта.

  • Это хочет знать, изменится ли значение объекта неожиданно, в то время как это имеет ссылку на него.

Используйте тип возврата, не самоанализ

Чтобы определить, может ли это изменить полученный объект, получатель сообщения должен полагаться на формальный тип возвращаемого значения. Если это получает, например, объект массива, введенный как неизменный, это не должно пытаться видоизменить его. Это не приемлемая практика программирования, чтобы определить, является ли объект непостоянным на основе своего членства в классе — например:

if ( [anArray isKindOfClass:[NSMutableArray class]] ) {
    // add, remove objects from anArray
}

По причинам, связанным с реализацией, что isKindOfClass: возвраты в этом случае могут не быть точными. Но по причинам кроме этого, Вы не должны делать предположения о том, является ли объект непостоянным на основе членства в классе. Ваше решение должно вестись исключительно тем, что подпись метода, продающего объект, говорит о его переменчивости. Если Вы не уверены, является ли объект непостоянным или неизменным, предположите, что это является неизменным.

Несколько примеров могли бы помочь разъяснить, почему эта инструкция важна:

  • Вы читаете список свойств из файла. Когда платформа Основы обрабатывает список, она замечает, что различные подмножества списка свойств идентичны, таким образом, она создает ряд объектов, которые она совместно использует среди всех тех подмножеств. Позже Вы смотрите на создаваемые объекты списка свойств и решаете видоизменить одно подмножество. Внезапно, и не будучи знающими об этом, Вы изменили дерево в многократных местах.

  • Вы спрашиваете NSView для его подпредставлений (с subviews метод) и это возвращает объект, который, как объявляют, является NSArray но который мог быть NSMutableArray внутренне. Тогда Вы передаете тот массив некоторому другому коду, через самоанализ, определяющему его, чтобы быть непостоянным и изменяющему его. Путем изменения этого массива код видоизменяет внутренние структуры данных NSView класс.

Не делайте предположение об объектной переменчивости на основе того, что самоанализ говорит Вам об объекте. Обработка возражает как непостоянная или не на основе того, что Вам вручают на границах API (т.е. на основе типа возврата). Если необходимо однозначно отметить объект как непостоянный или неизменный, когда Вы передаете его клиентам, передаете ту информацию как флаг вместе с объектом.

Сделайте снимки полученных объектов

Если Вы хотите гарантировать, что, предположительно, неизменный объект, полученный от метода, не видоизменяется без Вашего знания об этом, можно сделать снимки объекта путем копирования его локально. Тогда иногда сравнивайте сохраненную версию объекта с новой версией. Если объект видоизменился, можно скорректировать что-либо в программе, зависящей от предыдущей версии объекта. Перечисление 9-2 показывает возможную реализацию этого метода.

Перечисление 9-2  , Делающее снимок потенциально непостоянного объекта

static NSArray *snapshot = nil;
- (void)myFunction {
    NSArray *thingArray = [otherObj things];
    if (snapshot) {
        if ( ![thingArray isEqualToArray:snapshot] ) {
            [self updateStateWith:thingArray];
        }
    }
    snapshot = [thingArray copy];
}

Проблема с созданием снимков объектов для более позднего сравнения состоит в том, что это дорого. Вы обязаны делать многократные копии того же объекта. Более эффективная альтернатива должна использовать наблюдение значения ключа. Посмотрите, что Значение ключа Наблюдает Руководство по программированию для описания этого протокола.

Непостоянные объекты в наборах

Хранить непостоянные объекты в объектах коллекции может вызвать проблемы. Определенные наборы могут стать недопустимыми или даже повредить, если объекты, которые они содержат, видоизменяются, потому что путем видоизменения эти объекты могут влиять на способ, которым они размещаются в набор. Во-первых, свойства объектов, которые являются ключами в хешировании наборов такой как NSDictionary объекты или NSSet если измененные свойства будут влиять на результаты объекта, объекты, если изменено, повредят набор hash или isEqual: методы. (Если hash метод объектов в наборе не зависит от их внутреннего состояния, повреждение менее вероятно.) Второй, если объекту в упорядоченном наборе, таком как сортированный массив изменили его свойства, это могло бы влиять, как объект выдерживает сравнение с другими объектами в массиве, таким образом представляя недопустимое упорядочивание.