Создание и уничтожение объектов

В самом языке Objective-C нет специальных команд для создания и уничтожения объектов (подобных new и delete). Эта задача ложится на runtime-библиотеку и реализуется при помощи механизма посылки сообщений.

Реально используемой и наиболее широко распространенной схемой создания и уничтожения объектов в Objective-C является используемая в операционных системах NextStep и Mac OS X, которая и будет описана ниже.

Создание нового объекта разбивается на два шага — выделение памяти и инициализация объекта. Первый шаг реализуется методом класса alloc (реализованном в классе NSObject), который выделяет необходимое количество памяти (данный метод используется для выделения памяти не только для объектов класса NSObject, но и любого унаследованного от него класса). При этом в атрибут isa записывается указатель на class object соответствующего класса.

Обратите внимание, что сообщение alloc посылается class object-у требуемого класса и это сообщение возвращает указатель на выделенную под объект память.

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

id anObject = [[Rectangle alloc] init];

Приведённая выше конструкция является правильным способом создания объекта. Обратите внимание, что следующая конструкция может в ряде случаев не работать:

id anObject = [Rectangle alloc];

[anObject init];

Это связано с тем, что для ряда классов метод init может вернуть совсем другой указатель (а не self).

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

При создании нового класса обычно нет необходимости переопределять метод alloc, а вот необходимость переопределения метода init возникает достаточно часто.

Обратите внимание, что метод(ы) init является обычным методом, ничем не выделяющимся среди остальных (в отличие от С++, где конструктор — это особый метод, у которого например нельзя взять адрес).

Поэтому при создании нового класса и метода init вызов переопределенного метода init (при помощи [super init]) должен быть произведен явно в самом начале метода.

Довольно часто у объектов бывает сразу несколько методов, начинающихся с init, например init, initWithName:, initWithContentsOfFile: и т. д.

Установившейся практикой в таком случае является выделение среди всех init-методов одного, называемого designated initializer. Все остальные init-методы должны вызывать его и только он вызывает унаследованный init метод.

- initWithName: (const char *) theName   // designated initializer
{
    [super init];                        // call inherited method

    name = strdup ( theName );
}

- init
{
    return [self initWithName: ""];
}

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

+ (NSString *) initStringWithCString: (const char *) str;
+ (NSString *) initStringWithFormat: (NSString *) format, ...;

Mac OS X (как и NextStep) для управления времением жизни объектов используют reference counting — каждый объект содержит внутри себя некоторый счетчик, при создании устанавливаемый в единицу.

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

Установившейся практикой является посылка объекту сообщения retain всеми, заинтересованными в нём сторонами (объектами), то есть если вы запоминаете ссылку на объект, то следует послать ему сообщение retain.

Когда объект перестает быть нужен, то ему просто посылается сообщение release.

Данное сообщение уменьшает значение счетчика на единицу и, если это значение стало меньше единицы, уничтожает данный объект.

Перед уничтожением объекта ему посылается сообщение dealloc, позволяющее объекту произвести свою деинициализацию. При этом это также является обычным сообщением и в нём Вы явно должны в конце вызвать унаследованную реализацию через [super dealloc].

- (void) dealloc
{
    . . .
    [super dealloc];
}

Автор: Алексей Боресков

Метки:

Если вам понравилась статья, подпишитесь на RSS!
  • Teremock

    Реальное воплощение синкс диферент.
    Нормальные конструкторы существуют во всех нормальных ООП языках.
    Кроме обжект-це.
    Т.к. аппл думает по другому — идиотизм во главе угла.

    • http://devmac.ru Игорь

      По началу я тоже плевался. Но когда привыкаешь, становится удобно.