Инкапсуляция данных

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

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

Если объект должен поддержать ссылку к другому объекту через свойство, важно рассмотреть природу отношения между двумя объектами. Несмотря на то, что управление памятью для объектов Objective C главным образом обрабатывается для Вас через Automatic Reference Counting (ARC), важно знать, как избежать проблем как циклы сильной ссылки, приводящие к утечкам памяти. Эта глава объясняет жизненный цикл объекта и описывает, как думать с точки зрения управления Вашим графиком объектов через отношения.

Свойства инкапсулируют значения объекта

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

Объявите общедоступные свойства для представленных данных

Свойства Objective C предлагают способ определить информацию, которую класс предназначается для инкапсуляции. Как Вы видели в Доступе Управления Свойствами к Значениям Объекта, объявления свойства включены в интерфейс для класса, как это:

@interface XYZPerson : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

В этом примере, XYZPerson класс объявляет, что свойства строки содержат имя и фамилию лица.

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

Используйте методы доступа получить или установить значения свойств

Вы получаете доступ или устанавливаете свойства объекта через методы доступа:

    NSString *firstName = [somePerson firstName];
    [somePerson setFirstName:@"Johnny"];

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

Синтезируемые методы следуют определенным соглашениям о присвоении имен:

  • Метод раньше получал доступ к значению (метод получателя) имеет то же имя как свойство.

    Метод получателя для свойства вызывают firstName будет также вызван firstName.

  • Метод, используемый для установки значения (метод установщика), запускает со слова «набор» и затем использует капитализированное имя свойства.

    Метод установщика для свойства вызывают firstName будет вызван setFirstName:.

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

@property (readonly) NSString *fullName;

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

В этом случае компилятор синтезирует a fullName метод получателя, но не a setFullName: метод.

Если Вы хотите использовать другое имя для метода доступа, возможно указать пользовательское имя путем добавления атрибутов к свойству. В случае булево свойств (свойства, имеющие a YES или NO значение), это обычно для метода получателя запуститься со слова «,». Метод получателя для свойства вызывают finished, например, должен быть вызван isFinished.

Снова, возможно добавить атрибут на свойстве:

@property (getter=isFinished) BOOL finished;

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

@property (readonly, getter=isFinished) BOOL finished;

В этом случае компилятор синтезирует только isFinished метод, но не a setFinished: метод.

Точечный синтаксис является краткой альтернативой вызовам метода доступа

А также сделав явные звонки метода доступа, Objective C предлагает альтернативный точечный синтаксис для доступа к свойствам объекта.

Точечный синтаксис позволяет Вам свойствам доступа как это:

    NSString *firstName = somePerson.firstName;
    somePerson.firstName = @"Johnny";

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

  • Получение использования значения somePerson.firstName совпадает с использованием [somePerson firstName]

  • Установка использования значения somePerson.firstName = @"Johnny" совпадает с использованием [somePerson setFirstName:@"Johnny"]

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

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

По умолчанию, a readwrite свойство будет поддержано переменной экземпляра, которая будет снова синтезироваться автоматически компилятором.

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

Если Вы не указываете иначе, синтезируемая переменная экземпляра имеет то же имя как свойство, но с префиксом подчеркивания. Для вызванного свойства firstName, например, синтезируемую переменную экземпляра вызовут _firstName.

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

- (void)someMethod {
    NSString *myString = @"An interesting string";
 
    _someString = myString;
}

В этом примере это ясно это myString локальная переменная и _someString переменная экземпляра.

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

- (void)someMethod {
    NSString *myString = @"An interesting string";
 
    self.someString = myString;
  // or
    [self setSomeString:myString];
}

Исключение к этому правилу при записи инициализации, освобождения или пользовательских методов доступа, как описано позже в этом разделе.

Можно настроить синтезируемые имена переменной экземпляра

Как отмечалось ранее, поведение по умолчанию для writeable свойства состоит в том, чтобы использовать вызванную переменную экземпляра _propertyName.

Если Вы хотите использовать другое имя для переменной экземпляра, необходимо направить компилятор для синтезирования переменной с помощью следующего синтаксиса в реализации:

@implementation YourClass
@synthesize propertyName = instanceVariableName;
...
@end

Например:

@synthesize firstName = ivar_firstName;

В этом случае свойство все еще вызовут firstName, и будьте доступны через firstName и setFirstName: методы доступа или точечный синтаксис, но это будет поддержано вызванной переменной экземпляра ivar_firstName.

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

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

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

@interface SomeClass : NSObject {
    NSString *_myNonPropertyInstanceVariable;
}
...
@end
 
@implementation SomeClass {
    NSString *_anotherCustomInstanceVariable;
}
...
@end

Переменные экземпляра доступа непосредственно от методов инициализатора

Методы установщика могут иметь дополнительные побочные эффекты. Если Вы пишете свои собственные методы, они могут инициировать уведомления KVC, или выполнить дальнейшие задачи.

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

Типичное init метод похож на это:

- (id)init {
    self = [super init];
 
    if (self) {
        // initialize instance variables here
    }
 
    return self;
}

init метод должен присвоиться self к результату вызова метода инициализации суперкласса прежде, чем сделать его собственную инициализацию. Суперкласс может не инициализировать объект правильно и возврат nil таким образом, необходимо всегда проверять для проверки self не nil прежде, чем выполнить Вашу собственную инициализацию.

Путем вызова [super init] как первая строка в методе, объект инициализируется от его корневого класса вниз через каждый подкласс init реализация в порядке. Рисунок 3-1 показывает процесс для инициализации XYZShoutingPerson объект.

Рисунок 3-1  процесс инициализации

Как Вы видели в предыдущей главе, объект инициализируется любой путем вызова init, или путем вызова метода, инициализирующего объект с определенными значениями.

В случае XYZPerson класс, это было бы целесообразно предлагать метод инициализации, которые устанавливают начальное имя и фамилию лица:

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName;

Вы реализовали бы метод как это:

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName {
    self = [super init];
 
    if (self) {
        _firstName = aFirstName;
        _lastName = aLastName;
    }
 
    return self;
}

Определяемый Инициализатор является Основным Методом Инициализации

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

Если XYZPerson также имел свойство для даты рождения, определяемый инициализатор мог бы быть:

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName
                                            dateOfBirth:(NSDate *)aDOB;

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

- (id)initWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName {
    return [self initWithFirstName:aFirstName lastName:aLastName dateOfBirth:nil];
}

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

- (id)init {
    return [self initWithFirstName:@"John" lastName:@"Doe" dateOfBirth:nil];
}

Если необходимо записать метод инициализации при разделении на подклассы класса, использующего многократный init методы, необходимо или переопределить определяемый инициализатор суперкласса, чтобы выполнить собственную инициализацию или добавить собственный дополнительный инициализатор. Так или иначе необходимо вызвать определяемый инициализатор суперкласса (вместо [super init];) прежде, чем сделать любую Вашу собственную инициализацию.

Можно реализовать пользовательские методы доступа

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

Как пример, XYZPerson класс мог бы определить свойство только для чтения для полного имени лица:

@property (readonly) NSString *fullName;

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

- (NSString *)fullName {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}

Этот простой пример использует строку формата и спецификаторы (как описано в предыдущей главе) для создания строки, содержащей имя и фамилию лица, разделенное пространством.

Если необходимо записать пользовательский метод доступа для свойства, действительно использующего переменную экземпляра, необходимо получить доступ к той переменной экземпляра непосредственно из метода. Например, распространено задержать инициализацию свойства, пока это сначала не требуют, с помощью “ленивого средства доступа”, как это:

- (XYZObject *)someImportantObject {
    if (!_someImportantObject) {
        _someImportantObject = [[XYZObject alloc] init];
    }
 
    return _someImportantObject;
}

Прежде, чем возвратить значение, этот метод сначала проверяет ли _someImportantObject переменная экземпляра nil; если это, это выделяет объект.

Свойства являются атомарными по умолчанию

По умолчанию свойство Objective-C является атомарным:

@interface XYZObject : NSObject
@property NSObject *implicitAtomicObject;          // atomic by default
@property (atomic) NSObject *explicitAtomicObject; // explicitly marked atomic
@end

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

Поскольку внутренняя реализация и синхронизация атомарных методов доступа являются частными, не возможно объединить синтезируемое средство доступа с методом доступа, который Вы реализуете сами. При попытке, например, обеспечить пользовательский метод set для, Вы получите предупреждение компилятора atomic, readwrite свойство, но отпуск компилятор для синтезирования метода get.

Можно использовать nonatomic атрибут свойства, чтобы указать, что синтезировал средства доступа просто набор или возвращают значение непосредственно, без гарантий о том, что происходит, если к тому же самому значению получают доступ одновременно от различных потоков. Поэтому это быстрее к доступу a nonatomic свойство, чем atomic один, и хорошо комбинировать синтезируемый метод set, например, с Вашей собственной реализацией метода get:

@interface XYZObject : NSObject
@property (nonatomic) NSObject *nonatomicObject;
@end
@implementation XYZObject
- (NSObject *)nonatomicObject {
    return _nonatomicObject;
}
// setter will be synthesized automatically
@end

Управляйте графом объектов через владение и ответственность

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

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

В случае XYZPerson объект, например, эти два свойства строки для firstName и lastName эффективно «принадлежат» XYZPerson экземпляр. Это означает, что они должны оставаться в памяти настолько же долго как XYZPerson объект остается в памяти.

Когда один объект полагается на другие объекты таким образом, эффективно беря владение тех других объектов, первый объект, как говорят, имеет сильные ссылки к другим объектам. В Objective C объект поддерживается, пока это имеет по крайней мере одну сильную ссылку к нему от другого объекта. Отношения между XYZPerson экземпляр и два NSString объекты показаны на рисунке 3-2.

  Прочные отношения рисунка 3-2

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

Для добавления немного большего количества сложности к этому примеру считайте граф объектов для приложения как показанным на рисунке 3-3.

Рисунок 3-3  приложение Производителя Бейджа

Когда пользователь нажимает кнопку Update, предварительный просмотр значка обновляется с соответствующей информацией об имени.

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

Рисунок 3-4  Упрощенный граф объектов для начальной буквы создание КСИЗПЕРСОНА

Когда пользователь изменяет имя лица, граф объектов изменяется для сходства с рисунком 3-5.

Рисунок 3-5  Упрощенный граф объектов при изменении имени лица

Представление дисплея значка поддерживает прочные отношения к оригиналу @"John" представьте объект в виде строки, даже при том, что XYZPerson объект теперь имеет различное firstName. Это означает @"John" объект остается в памяти, используемой представлением значка для печати имени.

Как только пользователь нажимает кнопку Update во второй раз, представлению значка говорят обновить его внутренние свойства для соответствия объекта лица, таким образом, граф объектов похож на рисунок 3-6.

Рисунок 3-6  Упрощенный граф объектов после обновления представления значка

В этой точке, оригинале @"John" объект больше не имеет сильных ссылок к нему, таким образом, это удалено из памяти.

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

Избегите циклов сильной ссылки

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

Один очевидный пример потенциального ссылочного цикла существует между объектом табличного представления (UITableView для iOS и NSTableView для OS X) и его делегат. Для универсального класса табличного представления, чтобы быть полезным в многократных ситуациях, это делегирует некоторые решения к внешним объектам. Это означает, что полагается на другой объект решить, какое содержание это выводит на экран, или что сделать, если пользователь взаимодействует с определенной записью в табличном представлении.

Общий сценарий - то, что табличное представление имеет ссылку на своего делегата, и у делегата есть ссылка назад на табличное представление, как показано на рисунке 3-7.

  Сильные ссылки рисунка 3-7 между табличным представлением и его делегатом

Если другие объекты бросают свои прочные отношения к табличному представлению и делегату, как показано на рисунке 3-8, проблема происходит.

Рисунок 3-8  цикл сильной ссылки

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

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

Если табличное представление изменяется для использования слабого отношения для его делегата (который является как UITableView и NSTableView решите эту проблему), начальный граф объектов теперь похож на рисунок 3-9.

Рисунок 3-9  корректное отношение между табличным представлением и его делегатом

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

Рисунок 3-10  , Избегающий цикла сильной ссылки

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

Рисунок 3-11  , Освобождающий делегата

Как только делегат освобожден, больше нет никаких сильных ссылок к табличному представлению, таким образом, оно также освобождено.

Используйте сильные и слабые объявления для управления владением

По умолчанию, свойства объектов, объявленные как это:

@property id delegate;

используйте сильные ссылки для их синтезируемых переменных экземпляра. Для объявления слабой ссылки добавьте атрибут к свойству, как это:

@property (weak) id delegate;

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

    NSDate *originalDate = self.lastModificationDate;
    self.lastModificationDate = [NSDate date];
    NSLog(@"Last modification date changed from %@ to %@",
                        originalDate, self.lastModificationDate);

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

Если Вы не хотите, чтобы переменная поддержала сильную ссылку, можно объявить его как __weak, как это:

    NSObject * __weak weakVariable;

Поскольку слабая ссылка не поддерживает объект, для ссылочного объекта возможно быть освобожденным, в то время как все еще используется ссылка. Для предотвращения опасного висячего указателя к памяти, первоначально занятой теперь освобожденным объектом, слабая ссылка автоматически установлена в nil когда освобожден его объект.

Это означает это при использовании слабой переменной в предыдущем примере даты:

    NSDate * __weak originalDate = self.lastModificationDate;
    self.lastModificationDate = [NSDate date];

originalDate переменная может потенциально быть установлена в nil. Когда self.lastModificationDate повторно присваивается, свойство больше не поддерживает сильную ссылку к исходной дате. Если не будет никаких других сильных ссылок к нему, то исходная дата будет освобождена и originalDate набор к nil.

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

    NSObject * __weak someObject = [[NSObject alloc] init];

В этом примере недавно выделенный объект не имеет никаких сильных ссылок к нему, таким образом, это сразу освобождено и someObject установлен в nil.

Также важно рассмотреть импликации метода, который должен несколько раз получать доступ к слабому свойству, как это:

- (void)someMethod {
    [self.weakProperty doSomething];
    ...
    [self.weakProperty doSomethingElse];
}

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

- (void)someMethod {
    NSObject *cachedObject = self.weakProperty;
    [cachedObject doSomething];
    ...
    [cachedObject doSomethingElse];
}

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

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

    if (self.someWeakProperty) {
        [someObject doSomethingImportantWith:self.someWeakProperty];
    }

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

    NSObject *cachedObject = self.someWeakProperty;           // 1
    if (cachedObject) {                                       // 2
        [someObject doSomethingImportantWith:cachedObject];   // 3
    }                                                         // 4
    cachedObject = nil;                                       // 5

В этом примере сильная ссылка создается в строке 1, означая, что объект, как гарантируют, будет жив для проверочного вызова и вызова метода. В строке 5, cachedObject установлен в nil, таким образом, отказ от сильной ссылки. Если исходный объект не будет иметь никаких других сильных ссылок к нему в этой точке, то это будет освобождено и someWeakProperty будет установлен в nil.

Используйте небезопасные несохраненные ссылки для некоторых классов

Существует несколько классов в Касании Какао и Какао, еще не поддерживающих слабые ссылки, что означает, что Вы не можете объявить, что слабое свойство или слабая локальная переменная отслеживают их. Эти классы включают NSTextView, NSFont и NSColorSpace; для полного списка посмотрите Переход к Информации о версии ARC.

Если необходимо использовать слабую ссылку на один из этих классов, необходимо использовать небезопасную ссылку. Для свойства это означает использовать unsafe_unretained атрибут:

@property (unsafe_unretained) NSObject *unsafeProperty;

Для переменных необходимо использовать __unsafe_unretained:

    NSObject * __unsafe_unretained unsafeReference;

Небезопасная ссылка подобна слабой ссылке, в которой она не поддерживает свой связанный объект, но она не будет установлена в nil если освобожден целевой объект. Это означает, что Вас оставят с висячим указателем к памяти, первоначально занятой теперь освобожденным объектом, следовательно термин «небезопасный». Отправка сообщения к висячему указателю приведет к катастрофическому отказу.

Свойства копии поддерживают свои собственные копии

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

Как пример, класс взаимодействует через интерфейс для XYZBadgeView класс, показанный ранее на рисунке 3-4, мог бы быть похожим на это:

@interface XYZBadgeView : NSView
@property NSString *firstName;
@property NSString *lastName;
@end

Два NSString свойства объявляются, который оба поддерживают неявные сильные ссылки к их объектам.

Рассмотрите то, что происходит, если другой объект создает строку для установки как одно из свойств представления значка, как это:

    NSMutableString *nameString = [NSMutableString stringWithString:@"John"];
    self.badgeView.firstName = nameString;

Это совершенно допустимо, потому что NSMutableString подкласс NSString. Несмотря на то, что представление значка думает, что имеет дело с NSString экземпляр, это фактически имеет дело с NSMutableString.

Это означает, что может измениться строка:

    [nameString appendString:@"ny"];

В этом случае, несмотря на то, что именем был «Джон» в то время, когда оно было первоначально установлено для представления значка firstName свойство, это - теперь «Джонни», потому что была изменена непостоянная строка.

Вы могли бы выбрать это, представление значка должно поддержать свои собственные копии любого строкового набора для его firstName и lastName свойства, так, чтобы это эффективно получило строки в то время, когда установлены свойства. Путем добавления a copy припишите двум объявлениям свойства:

@interface XYZBadgeView : NSView
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;
@end

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

    NSMutableString *nameString = [NSMutableString stringWithString:@"John"];
    self.badgeView.firstName = nameString;
    [nameString appendString:@"ny"];

На сей раз, firstName сохраненный представлением значка будет незатронутая копия исходной строки «Джона».

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

Если необходимо установить a copy переменная экземпляра свойства непосредственно, например в методе инициализатора, не забывает устанавливать копию исходного объекта:

- (id)initWithSomeOriginalString:(NSString *)aString {
    self = [super init];
    if (self) {
        _instanceVariableForCopyProperty = [aString copy];
    }
    return self;
}

Упражнения

  1. Измените sayHello метод от XYZPerson класс для журналирования приветствия с помощью имени лица и фамилии.

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

    Не забывайте переопределять init вызывать определяемый инициализатор.

  3. Тест, что происходит при установке непостоянной строки как имени лица затем видоизмените ту строку прежде, чем вызвать измененный sayHello метод. Изменитесь NSString объявления свойства путем добавления copy атрибут и тест снова.

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

    Чтобы помочь проверить когда XYZPerson объект освобожден, Вы могли бы хотеть наброситься на объектный жизненный цикл путем обеспечения a dealloc метод в XYZPerson реализация. Этот метод вызывают автоматически, когда объект Objective C освобожден из памяти и обычно используется для выпуска любой памяти, Вы выделили вручную, такой как через C malloc() функция, как описано в Усовершенствованном Руководстве по программированию управления памятью.

    В целях этого осуществления переопределите dealloc метод в XYZPerson зарегистрировать сообщение, как это:

    - (void)dealloc {
        NSLog(@"XYZPerson is being deallocated");
    }

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

  5. Измените XYZPerson описание класса так, чтобы можно было отслеживать супруга или партнера.

    Необходимо будет решить, как лучше всего смоделировать отношение, думая тщательно об управлении графом объектов.