Basic Subclass Design
Объявление класса:
@interface Controller : NSObject {
// атрибуты
}
// методы
@end
При наследовании от некоторого полезного класса фреймворка придется переопределять некоторые методы. После этого нужно сделать так, чтобы эти методы были вызваны фреймворком вместе методов базового класса. В Interface Builder обычно для этого указывается порожденный класс вместе базового.
Instance Variables
Переменная с IBOutlet показывает, что внешние связи будут восстановлены автоматически при загрузке nib-файла.
Entry and Exit Points
Cocoa посылает объектам и классам, которые тоже являются объетком, в разные моменты жизни объекта определенные сообщения.
- initialize. Сообщение получает класс. Это самое первое сообщение, получаемое классом до того, как сообщения получат экземпляры. Сначала сообщение получает суперклассы, затем подклассы. Обычно используется для инициализации некоторого общего состояния, используемого всеми экземплярами.
- init. Инициализация экземпляра. Может быть будет достаточно выделенного инициализатора суперкласса.
- initWithCoder:/encodeWithCoder: Используется при десериализации, сериализации.
- awakeFromNib. Посылается всем объектам, загружаемым из nib-файла, но после того, как будут созданы все экземпляры объектов. Это гарантирует, что можно будет установить все указатели IBOutlet для данного объекта.
- dealloc or finalize. Деструкторы.
Объект приложения NSApp посылает сообщения
- applicationDidFinishLaunching:,
- applicationWillFinishLaunching:,
- applicationWillTerminate:
своему объекту-делегату, который может обработать эти ситуации.
Для сериализации/десериализации (архивирования, разархивирования) объект должен реализовать протокол NSCoding. При разархивировании ему будет послано сообщение initWithCoder:. Если объект нужно инициализировать не только при разархировании, то нужно написать обрабочик и для init.
Исключение: подкласс для класса фреймворка, связанный с Object в IB. В этом случае, экземпляру будет послано init, поэтому лучше всю инициализацию делать вообще в awakeFromNib.
Storing and Accessing Properties
Свойства могут быть двух видов: атрибуты и отношения.
Свойства могут хранить экземпляры (т.е. не обязательно хранят).
Taking Advantage of Declared Properties
Возможны два способа работы со свойствами: в случае использования garbage collector (1) и в случае memory-managed code (2).
2: В свойствах setter должны быть заданы атрибуты retain или copy. При использовании NSCopying всегда нужно указывать copy.
1: Генерируются setter для name и getter для accountID.
@interface MyClass : NSObject {
NSString *name;
NSNumber *accountID;
}
@property (copy) NSString *name;
@property (readonly) NSNumber *accountID;
// ...
@end
@implementation MyClass
@synthesize name, accountID;
// ...
@end
2: Для currentHost в getter будет увеличивать счетчик ссылок. Для hidden будет сгенерирован getter с заданным именем.
@interface MyClass : NSObject {
NSHost *currentHost;
Boolean *hidden;
}
@property (retain, nonatomic) NSHost *currentHost;
@property (getter=isHidden, nonatomic) Boolean *hidden;
// ...
@end
@implementation MyClass
@synthesize name, hidden;
// ...
@end
Implementing Accessor Methods
Методы getter и setter должны себя вести в соответствие с требованиями к управлению памятью. Setter должен позаботиться об освобождении памяти, занятой предыдущим значением.
Правила:
- Полученный из getter объект можно безопасно использовать в области видимости вызвавшего getter метода.
- Вызывающий getter метод не должен релизить полученный объект в своей области видимости, не отправив ему предварительно retain или copy.
- (NSString *)title {
return title;
}
- (void)setTitle:(NSString *)newTitle {
if (title != newTitle) {
[title autorelease];
title = [newTitle copy];
}
}
В примере выше возможна опасная ситуация: клиент получит указатель на title и почти одновременно с этим будет вызван setter, который авторелизнет title. Через некоторое время пул будет очищен и title перестанет существовать. Клиент останется при этом с недействительным указателем.
Вариант лучше:
- (NSString *)title {
return [[title retain] autorelease];
}
- (void)setTitle:(NSString *)newTitle {
if (title != newTitle) {
[title release];
title = [newTitle copy];
}
}
Счетчик ссылок на title в getter-е увеличен на 1 и title будет послано 1 сообщение release, когда будет чиститься пул. Поэтому клиент останется с действительным указателем даже после [title release] в setter-е.
Когда посылать retain, а когда copy при передаче объектов?
Для этого нужно ответить на вопрос: что нужно получить, сам объект как таковой или его значение? Сам объект возможно изменит своё значение, но это не страшно, если нам нужна именно сущность. Конкретное значение, например, строки вероятно более важно, чем просто какая-нибудь строка.
Для setter-а можно рассуждать так. Если он устанавливает значение атрибута объекта, т.е. некое примитивное значение типа строки, числа или цвета, то это значение нужно скопировать из передаваемого параметра. Если же устанавливается отношение, т.е. отношение с другим объектом, то скорее всего нужно послать параметру retain.
Если придерживать "ленивого" использования атрибутов, т.е. вызывать getter только, когда атрибут понадобится, то его значение нужно устанавливать также в этом getter. Т.е. setter нельзя вызывать явно.
Комментариев нет:
Отправить комментарий