Чтение онлайн

ЖАНРЫ

Программирование на Objective-C 2.0
Шрифт:

сначала высвобождают переменную экземпляра str и затем вызывают родитель-ский метод dealloc, чтобы закончить работу.

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

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

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

Решением этой проблемы является создание новой копии строки внутри метода-установщика, если вы хотите защитить ее и сделать совершенно неза-висимой от аргумента этого метода. Именно поэтому здесь выбрано создание копии компонентов name и email в методах setName: и setEmail: класса AddressCard (см. главу 15). 17.3. Пример автоматического высвобождения

Рассмотрим последний пример из этой главы, чтобы убедиться, что вы действи-тельно понимаете, как действует подсчет ссылок, удержание (retain) и высво- бождение/автоматическое высвобождение (release/autorelease) объектов. Рас-смотрим программу 17.6, которая определяет фиктивный класс с именем Foo с одной переменной экземпляра и только наследуемыми методами. #import <Foundation/NSObject.h> #import <Foundation/NSAutoreleasePool.h> @interface Foo: NSObject { int x; } @end @implementation Foo @end int main (int arge, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Foo *myFoo = [[Foo alloc] init]; NSLog (@"myFoo retain count = %x", [myFoo retainCount]); [pool drain]; NSLog (@"after pool drain = %x", [myFoo retainCount]); pool = [[NSAutoreleasePool alloc] init]; [myFoo autorelease]; NSLog (@"after autorelease = %x", [myFoo retainCount]); [myFoo retain]; NSLog (@"after retain = %x", [myFoo retainCount]); [pool drain]; NSLog (@"after second pool drain = %x", [myFoo retainCount]); [myFoo release]; return 0; }

Вывод программы 17.6 myFoo retain count = 1 (счетчик ссылок myFoo) after pool drain = 1 (после pool drain) after autorelease = 1 (после autorelease) after retain = 2 (после retain) after second pool drain = 1 (после второго pool drain)

Программа выделяет память для нового объекта Foo и присваивает его пере-менной myFoo. Как вы уже видите, начальное значение ее счетчика удержаний (ссылок) равно 1. Этот объект еще не является частью autorelease-пула, поэтому высвобождение этого пула не делает объект недействительным. Затем выделяется новый пул, и myFoo добавляется в этот пул путем отправки сообщения autorelease. Снова отметим, что счетчик ссылок этой переменной не изменяется, так как добавление объекта в autorelease-пул не влияет на его счетчик ссылок, а только помечает его для дальнейшего высвобождения.

Затем мы передаем myFoo сообщение retain. Это изменяет ее счетчик ссылок на 2. При последующем высвобождении пула во второй раз счетчик ссылок для myFoo уменьшается на 1, так как ранее ей было передано сообщение autorelease и, следовательно, ей передается сообщение release, когда происходит высвобождение пула.

Поскольку для myFoo было выполнено удержание (retain) до высвобождения пула, ее счетчик ссылок после уменьшения будет все еще больше 0. Поэтому myFoo остается после pool drain и все еще является действительным объектом. Конечно, вы должны теперь высвободить и ее, что мы и делаем в программе 17.6 для очистки и ликвидации утечек памяти.

Если вам понятна программа 17.6, значит, вы хорошо понимаете, что такое autorelease-пул и как он действует. 17.4. Сводка правил по управлению памятью

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

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

Автоматически высвобождаемый

пул (autorelease-пул) предусматривает ав-томатическое высвобождение объектов, когда высвобождается сам пул. Си-стема делает это, передавая сообщение release каждому объекту столько же раз, сколько было передано сообщений autorelease. Каждому объекту в autorelease-пулс, счетчик ссылок которого уменьшился до 0, передается со-общение dealloc для ликвидации самого объекта.

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

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

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

В случае непосредственного создания объекта с помощью метода alloc или сору (либо метода allocWithZone:, copyWithZone: или mutableCopy) вы обязаны сами высвободить его. Каждый раз при удержании объекта (с помощью retain) вы должны применить release или autorelease к этому объекту.

Вам не нужно заботиться о высвобождении об'ьектов, возвращаемых методами, которые не упомянуты в предыдущем правиле. Такие методы будут сами обеспечивать автоматическое высвобождение объектов. Именно поэтому вам нужно в первую очередь создавать autorelease-пул в своих программах. Такие методы, как stringWithString:, автоматически добавляют в этот пул новые создаваемые строковые объекты, передавая им сообщения autorelease. Если не создать пул, то вы получите сообщение, что пытаетесь автоматически высвободить (autorelease) объект при отсутствии пула. 17.5. Сборка мусора

До настоящего момента мы создавали программы для выполнения в среде runtime с управлением памятью (memory-managed environment). Правила управления памятью, изложенные в предыдущих разделах, применимы к такой среде, где используются autorelease-пулы и возникают вопросы высвобождения и удержания объектов, а также владения объектами.

В Objective-C 2.0 стала доступна альтернативная форма управления памятью, которая называется сборкой мусора (garbage collection). Используя сборку мусора, можно не думать об удержании и высвобождении объектов, autorelease- пулах или счетчиках удержаний (ссылок). Система автоматически следит, какие объекты владеют другими объектами, автоматически высвобождает (то есть включает в сборку мусора) объекты, к которым больше нет обращений, по мере того, как возникает потребность в памяти при выполнении программы.

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

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

И третья причина относится к вам, если вы планируете писать библиотечные процедуры, встраиваемые модули (plug-in) или код для совместного доступа. Этот код может быть загружен в процесс как со сборкой мусора, так и без сборки мусора, поэтому его следует писать для работы в обеих средах. Это означает, что вы должны использовать методы управления памятью, описанные в этой книге. Это также означает, что вы должны тестировать свой код с отключением и включением сборки мусора.

Поделиться с друзьями: