Интернет магазин китайских планшетных компьютеров



Компьютеры - Objective-C - Управление памятью

06 июля 2011


Оглавление:
1. Objective-C
2. Синтаксис языка
3. Создание новых классов
4. Как работает механизм сообщений
5. Протоколы
6. Обработка исключений
7. Создание и уничтожение объектов
8. Управление памятью
9. Категории
10. Class objects и Objective-C runtime
11. Разное



Базовые принципы

Управление памятью в Objective C базируется на принципе "владения объектом". Основные правила управления памятью в Objective C можно записать так:

  • Для получения объекта во владение необходимо вызвать метод, содержащий в названии "alloc", "new" либо "copy". Например, alloc, newObject, mutableCopy.
  • Для освобождения объекта, который был получен при помощи перечисленных выше функций, необходимо вызвать функцию "release" либо "autorelease". Во всех остальных случаях освобождение объекта не требуется.
  • Если полученный объект должен быть сохранен, необходимо либо стать его владельцем, либо создать его копию.

Данные правила базируются на соглашении по именованию в Objective C и, в то же время, сами являются основой этого соглашения.

Базовые принципы на практике

Предположим, в программе существует класс Company, у которого есть метод workers.

@interface Company : NSObject
{
    NSArray *workers;
}
-workers;
@end

Рассмотрим небольшой пример использования такого класса:

Company *company =  init];
// ...
NSArray *workers = ;
// ...
;

Так как объект класса Company создается явно, он должен быть удален по окончании использования. В то же время, название метода workers не говорит о том, кто должен удалять массив. В такой ситуации считается, что списком работников управляет объект Компания и его удалять не требуется.

Convenience конструкторы

Многие классы позволяют совместить создание объекта с его инициализацией при помощи методов, называемых convenience конструкторы; такие методы обычно называются +className... Можно предположить, что вызывающая сторона ответственна за управление временем жизни объекта, но подобное поведение противоречило бы соглашению по именованию в Objective C.

Company *company = ;
;

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

Ниже приводится пример корректной реализации метода company:

+company
{
     id ret =  init];
     return ;
}
autorelease

Вернемся к методу workers класса Company. Так как возвращается массив, временем жизни которого вызывающая сторона не управляет, реализация метода workers будет выглядеть приблизительно так:

-workers
{
     NSArray *copy =  initWithArray:workers];
     return ;
}

Вызов autorelease добавляет объект copy в autorelease пул, вследствие чего возвращаемый объект получит сообщение release при удалении пула, в который он был добавлен. Если объекту, добавленному в autorelease пул, послать сообщение release самостоятельно, при удалении autorelease пула возникнет ошибка.

Возвращение объекта по ссылке

В ряде случаев объекты возвращаются по ссылке, например, метод класса NSData initWithContentsOfURL:options:error: в качестве параметра error принимаетerrorPtr. В этом случае так же работает соглашение по именованию, из которого следует, что явного запроса на владение объектом нет, соответственно, удалять его не требуется.

Удаление объектов

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

-dealloc
{
    ;
    ;
}

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

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

Autorelease pool

Autorelease пул используется для хранения объектов, которым будет послано сообщение release при удалении пула. Для того, чтобы добавить объект в autorelease пул, ему необходимо отправить сообщение autorelease.

В приложениях Cocoa autorelease пул всегда доступен по умолчанию. Для не-AppKit приложений необходимо создавать и управлять временем жизни autorelease пула самостоятельно.

Autorelease пул реализуется классом NSAutoreleasePool.

int main 
{
    NSAutoreleasePool * pool =  init];
 
     Company *company = ;
     NSArray *workers = ;
 
    ;
    return 0;
}

Удалить объекты из autorelease пула можно не только посредством отправки пулу сообщения release, но и с помощью сообщения drain. Поведение release и drain в среде с подсчетом ссылок идентично. Но в случае работы в GC среде drain вызывает функцию objc_collect_if_needed.

Autorelease пул в многопоточной среде

В Cocoa для каждого из потоков создается свой собственный autorelease пул. По завершении потока autorelease пул уничтожается и всем содержащимся в нем объектам посылается сообщение release.

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

Копирование объектов

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

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

Пример реализации

Реализация копирования может различаться в зависимости от того, поддерживает ли класс-родитель протокол NSCopying. Пример кода для ситуации, когда родитель не реализует протокол NSCopying:

@interface Company : NSObject <NSCopying>
{
     NSString *name;
}
@property NSString *name;
-copyWithZone:zone;
@end
 
@implementation Company
@synthesize name;
-copyWithZone:zone
{
     id copy =  allocWithZone:zone] init];
     ];
     return copy;
}
@end

Если родитель поддерживает протокол NSCopying, реализация будет несколько иной: вызов allocWithZone заменяется на copyWithZone.

id *copy = ;
Копирование неизменяемых объектов

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

-copyWithZone:zone
{
     return ;
}


Просмотров: 10625


<<< Mission Control
Photo Booth >>>