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

ЖАНРЫ

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

Отметим, что при опустошении пула (pool drain)autorelease-nyji содержит не сами обьекты, а только ссылку на объекты. Чтобы добавить объект втекуши й autorelease- пул для последующего высвобождения, нужно отправить сообщение autorelease. [myFraction autorelease];

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

Описывая базовый класс объектов Objective-C NSObject, мы говорили, что память выделяется с помощью метода alloc, и ее можно в дальнейшем освободить с помощью сообщения release. К сожалению, это не всегда так просто. Выполняемое приложение может ссылаться на объект, который может быть создан в нескольких местах; объект может быть также сохранен в массиве или, например, к нему может быть обращение с помощью переменной экземпляра.

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

К счастью, Foundation framework включает удобное решение для отслежи-вания числа ссылок на объект. Это довольно простой способ, который называется подсчетом ссылок (reference count). Он состоит в следующем. При создании объекта его счетчик ссылок устанавливается равным 1. Каждый раз, когда нужно учесть объект, мы увеличиваем его счетчик ссылок на 1, отправляя сообщение retain, как в следующей строке. [myFraction retain];

Некоторые методы в Foundation framework тоже наращивают этот счетчик ссылок, например, когда объект добавляется в массив.

Когда объект уже не нужен, мы уменьшаем на I его счетчик ссылок, отправляя сообщение release, как в следующей строке. [myFraction release];

Когда счетчик ссылок объект становится равным 0, система «понимает», что этот объект больше не нужен (поскольку на него нет больше ссылок), и поэтому она освобождает (deallocate) его память. Для этого объекту отправляется со-общение dealloc.

Успешное осуществление этой стратегии требует аккуратности от програм-миста, чтобы счетчик ссылок правильно наращивался и уменьшался во время выполнения программы. Как вы увидите ниже, система выполняет только часть этой работы.

Рассмотрим подсчет ссылок несколько подробнее. Объекту можно отправить сообщение retainCount, чтобы получить его счетчик ссылок (или удержаний, retain). Обычно вы не будете использовать этот метод, но здесь мы рассмотрим его в иллюстративных целях (см. программу 17.1). Отметим, что он возвращает целое без знака (unsigned int) типа NSUInteger. // Знакомство с подсчетом ссылок #import <Foundation/NSObject.h> #import < Foundation/NSAutoreleasePool.h> #import <Foundation/NSString.h> #import <Foundation/NSArray.h> #import <Foundation/NSValue.h> int main (int arge, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSNumber *mylnt = [NSNumber numberWithlnteger: 100]; NSNumber *mylnt2; NSMutableArray *myArr = [NSMutableArray array]; NSLog (@"mylnt retain count = %lx", (unsigned long) [mylnt retainCount]); [myArr addObject: mylnt]; NSLog (@"after adding to array = %lx", (unsigned long) [mylnt retainCount]); mylnt2 = mylnt; NSLog {@"after asssignment to mylnt2 = %lx’\ (unsigned long) [mylnt retainCount]); [mylnt retain]; NSLog (@"mylnt after retain = %lx", (unsigned long) [mylnt retainCount]); NSLog (@"mylnt2 after retain = %lx", (unsigned long) [mylnt2 retainCount]); [mylnt release]; NSLog (@“after release = %lx") (unsigned long) [mylnt retainCount]); [myArr removeObjectAtlndex: 0]; NSLog (@nafter removal from array = %lx", (unsigned long) [mylnt retainCount]}; [pool drain]; return 0; }

Вывод программы 17.1 mylnt retain count = 1 (счетчик удержаний mylnt) after adding to array = 2 (после добавления в массив) after asssignment to mylnt2 = 2 (после присваивания mylnt2) mylnt after retain = 3 (mylnt после удержания) mylnt2 after retain = 3 (mylnt2 после удержания) after release = 2 (после release) after removal from array = 1 (после удаления из массива)

Объекту NSNumber mylnt присваивается целое значение 100, и вывод показы-вает, что начальное число его удержаний равно 1. Затем этот объект добавляется в массив myArr с помощью метода addObject:. Обратите внимание, что после этого его счетчик ссылок равен 2. Метод addObject: делает это автоматически; в документации по addObject: описан этот факт. Добавление объекта в любой тип коллекции увеличивает его счетчик ссылок. Это означает, что когда мы высво-бождаем (release) добавленный ранее объект, на него можно будет по-прежнему ссылаться из массива, и он не будет высвобожден.

Затем мы присваиваем mylnt переменной mylnt2. Отметим, что это не приво-дит к наращиванию счетчика ссылок, что может вызвать в дальнейшем потен-циальные проблемы. Например, если счетчик ссылок для mylnt уменьшится до 0 и его память будет освобождена, mylnt2 будет содержать неверную ссылку на объект (напомним, что присваивание mylnt переменной mylnt2 не приводит к копированию самою объекта, а только к созданию указателя на место в памяти, где находится сам объект).

Поскольку mylnt теперь имеет еще одну ссылку (через mylnt2), мы наращиваем его счетчик ссылок, отправляя ему сообщение retain. Это происходит в следующей сгроке программы 17.1. Как мы

видим, после отправки сообщения retain счетчик ссылок становится равным 3. Первая ссылка — это сам объект, вторая ссылка делается из массива и третья — во время присваивания. Сохранение элемента в массиве вызывает автоматическое наращивание счетчика ссылок, а присваивание другому элементу — нет, поэтому мы должны сделать это сами. Отметим, что при выводе счетчик ссылок mylnt и на mylnrt2 дает одинаковое значение 3; дело в том, что в обоих случаях это ссылка на один и тот же объект в памяти.

Предположим, что мы прекратили использовать объект mylnt в программе. Это можно сообщить системе, отправив объекту сообщение release. Как мы можем видеть, его счетчик ссылок в результате уменьшается с 3 до 2. Счетчик нс равен 0; это означает, что продолжают действовать другие ссылки (из массива и через mylnt2). Система не освобождает память, используемую этим объектом, поскольку счетчик ссылок не равен нулю.

После удаления первого элемента из массива myArr с помощью метода removeObjectAtlndex: мы видим, что счетчик ссылок автоматически уменьшился до В общем случае удаление объекта из любой коллекции сопровождается уменьшением на 1 его счетчика ссылок. Поэтому следующая последовательность кода может вызвать проблемы. mylnt = [myArr ObjectAtlndex: 0]; [myArr removeObjectAtlndex: 0]

Дело в том, что в данном случае объект, на который ссылается mylnt, может стать недействительным после вызова метода removeObjectAtlndex:, если его счет-чик ссылок уменьшился до 0. Конечно, для решения этой проблемы нужно удер-жать (retain) mylnt после считывания из массива, чтобы на его ссылку не повлияло то, что происходит в других местах. Подсчет ссылок и строки

В программе 17.2 показано, как действует подсчет ссылок для строковых объектов. // Подсчет ссылок в случае строковых объектов. #import <Foundatton/NSObject.h> #import <Foundation/NSAutoreleasePool.h> Simport <Foundation/NSString.h> #import <Foundation/N$Array.h> int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSString *myStr1 = @"Constant string"; NSString *myStr2 = [NSString stringWithString: @"string 2"]; NSMutableString *myStr3 = [NSMutableString stringWithString: @"string 3"]; NSMutableArray *myArr = [NSMutableArray array]; NSLog (@"Retain count: myStrl: %lx, myStr2: %lx, myStr3: %lx", (unsigned long) [myStrl retainCount], (unsigned long) [myStr2 retainCount], (unsigned long) [myStr3 retainCount]); [myArr addObject: myStrl]; [myArr addObject: myStr2]; [myArr addObject: myStr3]; NSLog (@"Retain count: myStrl: %lx, myStr2: %lx, myStr3: %lx", (unsigned long) [myStrl retainCount], (unsigned long) [myStr2retainCount], (unsigned long) [myStr3 retainCount]); [myArr addObject: myStrl]; [myArr addObject: myStr2]; [myArr addObject: myStr3]; NSLog (@"Retain count: myStrl: %lx, myStr2: %lx, myStr3: %lx", (unsigned long) [myStrl retainCount], (unsigned long) [myStr2retainCount], (unsigned long) [myStr3 retainCount]); [myStrl retain]; [myStr2 retain]; [myStr3 retain]; NSLog (@"Retain count: myStrl: %lx, myStr2: %lx, myStr3: %lx", (unsigned long) [myStrl retainCount], (unsigned long) [myStr2 retainCount], (unsigned long) [myStr3 retainCount]); // Уменьшение счетчика ссыпок myStr3 снова до 2 [myStr3 release]; [pool drain]; return 0; }

Вывод программы 17.2 Retain count: myStrl: ffffffff, myStr2: ffffffff, myStr3: 1 (Счетчик ссылок:) Retain count: myStrl: ffffffff, myStr2: ffffffff, myStr3: 2 Retain count: myStrl: ffffffff, myStr2: ffffffff, myStr3: 3

Объекту NSString myStrl присваивается строка NSConstantString @'Constant string" (Константная строка). Вьшеление места в памяти для константных строк отличается от других объектов. Константные строки не имеют механизма подсчета ссылок, поскольку их нельзя высвободить. Именно поэтому при отправке сообщения retainCount переменной myStrl счетчик возвращает значение Oxffffffff. (Это на самом деле максимально возможное целое значение без знака, то есть UINT_MAX в стандартном header-файле <limits.h>.)

Примечание. Очевидно, что в некоторых системах счетчик ссылок, возвращаемый для константных строк в программе 17.2, дает значение Qx7fffffif (а не Gxffffffff), что является максимально возможным целым значением со знаком, то есть INT_MAX.

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

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

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