355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Стивен Кочан » Программирование на Objective-C 2.0 » Текст книги (страница 22)
Программирование на Objective-C 2.0
  • Текст добавлен: 19 сентября 2016, 13:02

Текст книги "Программирование на Objective-C 2.0"


Автор книги: Стивен Кочан



сообщить о нарушении

Текущая страница: 22 (всего у книги 32 страниц)

Возможно, вам потребуется также функция Foundation NSSearchPathForDPrectorieslnDomains для обнаружения в системе специальных папок, таких как Application. Копирование файлов и использование класса NSProcessInfo

В программе 16.6 показано средство командной строки для реализации простой операции копирования файлов. Эта команда применяется следующим образом. copy from-file {bcfjlysq afqk) to-file (afqk-rjgbz)

В отличие от метода NSFileManager copyPath:toPath:handler:, это средство ко-мандной строки позволяет также использовать to-file как имя папки. Тогда эта папка копируется в папку to-file под именем from-file. Еще одно отличие от ука-занного метода: если to-file уже существует, его содержимое перезаписывается. Это в большей степени согласуется со стандартной командой копирования UNIX (ср).

Для получения имен файлов из командной строки можно использовать ар-гументы argc и argv, передаваемые в main. В качестве этих аргументов в команд-ной строке указываются, соответственно, целый ряд типов аргументов (включая имя команды) и указатель на массив символьных С-строк.

Вместо обработки С-строк, что приходится делать при работе с argv, исполь-зуйте класс Foundation NSProcessinfo. NSProcessInfo содержит методы, позволяющие задавать и считывать различные типы информации о выполняемом приложении (то есть вашем процессе). Эти методы приводятся в таблице 16.5.

Табл. 16.5. Методы класса NSProcessinfo Метод Описание +(NSProcessInfo *) processlnfo Возвращает информацию о текущем процессе. -(NSArray *) arguments Возвращает аргументы для текущего процесса в виде массива объектов NSString. -(NSDictionary *) environment Возвращает словарь, состоящий из пар «переменная/значение», представляющих текущие переменные среды (например, PATH и НОМЕ) вместе с их значениями. -(int) processldentifier Возвращает идентификатор процесса, то есть уникальный номер, назначаемый операционной системой для идентификации каждого выполняемого процесса. -(NSString *) processName Возвращает имя текущего выполняемого процесса. -(NSString *) globallyllniqueString Возвращает при каждом вызове новую уникальную строку. Это можно использовать для генерации уникальных имен временных файлов (см. упражнение 5). -(NSString *) hostName Возвращает имя хост-системы (возвращает SteveKochans-Computer.local в моей системе Mac OS X). -(NSUInteger) operatingSystem Возвращает число, обозначающее операционную систему (возвращает значение 5 на моем Маке). -(NSString *) operatingSystemName Возвращает имя операционной системы (возвращает константу NSMACHOperatingSystem на моем Маке, где возможные возвращаемые значения определены в NSProcessInfo.h). -(NSString *) operatingSystemVersionString Возвращает текущую версию операционной системы (возвращает Version 10.5.4 (Build 9Е17) в моей системе Mac OS X). -(void) setProcessName: (NSString *) name Задает имя name для текущего процесса. Следует использовать с осторожностью, поскольку необходимо учитывать некоторые предположения об имени вашего процесса (например, в настройках пользователя по умолчанию). // Реализация простой утилиты копирования #import #import #import #import #import #import int main {int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSFileManager *fm; NSString *source, *dest; BOOL isDir; NSProcessInfo *proc = [NSProcessInfo processlnfo]; NSArray *args = [proc arguments]; fm = [NSFileManager defaultManager]; // Проверка того, что в командной строке заданы два аргумента if ([args count] != 3) { NSLog (@"Usage: %@ src dest", [proc processName]); return 1; } source = [args objectAtlndex: 1]; dest = [args objectAtlndex: 2]; // Проверка того, что исходный файл можно читать if ([fm isReadableFileAtPath: source] == NO) { NSLog (@"Can't read %@", source); return 2; } // Является ли целевой файл (dest) папкой? // Если да, то исходный файл (source) добавляется в конец целевого пути ffm fileExistsAtPath: dest isDirectory: &isDir); if (isDir == YES) dest = [dest stringByAppendingPathComponent: [source lastPathComponent]]; // Удаление целевого файла, если он уже существует [fm removeFileAtPath: dest handler: nil]; // Все сделано, можно выполнять копирование if ([fm copyPath: source toPath: dest handler: nil] == NO) { NSLog (@"Copy failed!"); return 3; } NSLog (@"Copy of %@ to %@ succeeded!", source, dest); [pool drain]; return 0; }

Вывод программы 16.6 $ Is -I смотрим, какие файлы у нас есть total 96 -rwxr-xr-x 1 stevekoc staff 19956 Jul 24 14:33 copy -rw-r~r~1 stevekoc staff 1484 Jul 24 14:32 copy.m -rw-r-r– 1 stevekoc staff 1403 Jul 24 13:00 filel.m drwxr-xr-x 2 stevekoc staff 68 Jul 24 14:40 newdir -ПАГ-Г-Г–1 stevekoc staff 1567 Jul 24 14:12 pathl.m -rw-r-r–1 stevekoc staff 84 Jul 24 13:22 testfile $ copy попытка команды без аргументов Usage: copy src dest (Использование: ...) $ copy foo copy2 Can’t read foo (Невозможно прочитать foo) $ copy copy.m backup.m Copy of copy.m to backup.m succeeded! (Копирование copy.m в backup.m выполнено!) $ diff copy.m backup.m сравнение файлов $ copy copy.m newdir попытка копирования в папку Copy of copy.m to newdir/copy.m succeeeded! (Копирование copy.m в newdir/copy.m выполнено!) $ Is -I newdir total 8 -rw-r-r– 1 stevekoc staff 1484 Jul 24 14:44 copy.m $

Метод NSProcessInfo arguments возвращает массив строковых объектов. Первый элемент этого массива – имя процесса, остальные элементы содержат аргументы, которые вводятся в командной строке.

Сначала проверяем, что в командной строке введено два ар^менга. Для этого проверяем размер массива args, возвращаемого методом arguments. Если проверка дает правильный результат, то программа затем извлекает имена исходного и целевого файлов из массива args, присваивая их значения переменным source и dest соответственно.

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

Оператор tfm fileExistsAtPath: dest isDirectory: ftsDir];

проверяет файл, который указывается переменной dest, чтобы определить, не является ли он папкой. Как вы видели выше, ответ (YES или N0) сохраняется в переменной isDir.

Если dest является папкой, то нужно добавить в конец имени этой папки имя исходного файла в качестве последнего компонента пути. Для этого используется метод работы с папками stringByAppendingPathComponent:. Например, если значением переменной source является строка chl6/copy1.m, а значением пере-менной dest является /Users/stevekochan/progs, и это папка, то нужно изменить значение dest на /Users/steveкосhan/progs/coру 1 .т.

Метод copyPath:ToPath:handler: не допускает перезаписи файлов. Таким обра-зом, чтобы избежать ошибки, программа пытается удалить с начала целевой файл с помощью метода removeFileAtPath:handler:* На самом деле не имеет значения, выполнит л и эту работу данный метод, поскольку он в любом случае выдаст ошибку, если целевой файл не существует.

Если достигнут конец программы, то предполагается, что задача выполнена, и выдается соответствующее сообщение. Основные файловые операции: NSFileHandle

Методы из класса NSFileHandle позволяют выполнять более сложную работу с файлами. В начале этой главы мы перечислили некоторые возможности этих методов.

В общем случае при работе с файлом нужно выполнить следующие шаги.

Открыть файл и получить объект класса NSFileHandle для ссылки на этот файл в последующих операциях ввода-вы вода.

Выполнить необходимые операции ввода-вывода с открытым файлом.

Закрыть файл.

В таблице 16.6 приводятся некоторые наиболее распространенные методы NSFileHandle. В этой таблице fh – это объект NSFileHandle, data – это объект NSData, path – это объект NSString, offset – это значение типа unsigned long long. Метод Описание +(NSFileHandle *) fileHandleForReadingAtPath: path Открывает файл для чтения. +(NSFileHandle *) fileHandleFoiWritingAtPath: path Открывает файл для записи. +(NSFileHandle *) fileHandleForUpdatingAtPath: path Открывает файл для изменения (чтение и запись). -(NSData *) availableData Возвращает данные, доступные для чтения, из устройства или канала. -(NSData *) readDataToEndOfFile Читает оставшиеся данные до конца файла порциями не более (UINT_MAX) байт. -(NSData *) readDataOflength: (NSUInteger) bytes Читает заданное количество байтов {bytes) из файла. -(void) writeData: data Записывает данные (data) в файл. -(unsigned long long) offsetlnFite Получает текущее смещение в файле -(void) seekToFileOffset: offset Задает текущее смещение (offset) в файле. -(unsigned long long) seekToEndOfFile Помешает текущее смещение в конец файла. -(void) truncateFileAtOffset: offset Задает размер файла, равный o/fee/байт (с заполнением при необходимости свободного места). -(void) closeFile Закрывает файл.

Здесь не приводятся методы NSFileHandle для стандартного ввода, стандарт-ного вывода, стандартных ошибок и null-устройства. Они имеют форму fileHandleWithDevice, где обозначение Device может быть представлено как Standardlnput, StandardOutput, Standard Error или NullDevice.

Здесь также не приводятся методы для чтения и записи данных в фоновом режиме, то есть асинхронно.

Следует отметить, что класс FileHandle не предусматривает создание файлов. Это нужно делать с помощью методов FtleManager, которые были описаны выше. Например, в методах fileHandleForWritingAtPath: и fileHandleForUpdatingAlF’ath: предполагается, что файл существует, и они возвращают значение nil, если его нет. В обоих случаях смешение задастся в начале файла, поэтому запись (или чтение в режиме update) начинается с начала файла. Кроме того, если вы работали с UNIX, то, видимо, обратили внимание, что открытие файла для записи не вызывает усече-ния файла. Вы должны сделать это самостоятельно, если это вам нужно.

Программа 16.7 открывает исходный файл testfile, который мы создали в на-чале главы, читает его содержимое и копирует его в файл с именем testout. // Основные операции обработки файлов // Предполагается, что существует файл с именем "testfile" // в текущей рабочей папке  #import #import #import #import #import #import int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSFileHandle *inFile, *outFile; NSData *buffer; // Открытие файла testfile для чтения inFile = [NSFileHandle fileHandleForReadingAtPath: @"testfile"]; if (inFile == nil) { NSLog (@"Open of testfile for reading failed"); (Невозможно открыть testfile для чтения) return 1; } // Создание выходного файла, если это нужно [[NSFileManager defaultManager] createFileAtPath: @"testout" contents: nil attributes: nil]; // Теперь открытие выходного файла для записи outFile = [NSFileHandle fileHandleForWritingAtPath: @"testout"]; if (outFile == nil) { NSLog (@"Open of testout for writing failed"); (Невозможно открыть testfile для записи) return 2; } // Усечение выходного файла, поскольку он может содержать данные [outFile truncateFileAtOffset: 0]; // Чтение данных из inFile и запись в outFile buffer = [inFile readDataToEndOfFile]; [outFile writeData: buffer]; // Закрытие обоих файлов [inFile closeFile]; [nutFilfi nloseFilfi]; // Проверка содержимого файла NSLog(@"%@", [NSString StringWithContentsOfFile: @"testout" encoding: NSUTF8StringEncoding error: nil]); [pool drain]; return 0; }

Вывод программы 16.7 This is a test file with some data in it. (Это тестовый файл с некоторыми данными.) Here’s another line of data. (Это еще одна строка данных.) And a third. (И третья.)

Метод readDataToEndOfFile: читает данные порциями не более UINT_MAX байт, что определено в и равно FFFFFFFF,6. Этого вполне достаточно для приложения, которое требуется написать. Мы можем также разбить эту работу для чтения и записи небольшими порциями и задать цикл для единовременной пересылки полного буфера между файлами с помощью метода readDataOfLength:. Например, размер буфера можно задать равным 8192 (8 кбайт) или 131072 (128 кбайт). Как правило, размер равен степени 2, поскольку базовая операционная система обычно выполняет свои операции ввода-вывода именно такими порциями данных. Вы можете опробовать различные значения, чтобы определить, что лучше в вашей системе.

Если метод чтения обнаруживает конец файла без чтения каких-либо данных, он возвращает пустой объект NSData (то есть буфер, не содержащий никаких данных). Мы можем применять к буферу метод length и проверять его на равенство нулю, чтобы определить, остались ли какие-то данные для чтения из файла.

Если мы открываем файл для изменения, смешение устанавливается на начало файла. Это смешение можно изменить путем поиска внутри файла и затем выполнить в файле операции чтения или записи. Например, для поиска 10-го байта в файле с описателем databaseHandle можно написать в сообщении следующее выражение. [databaseHandle seekToFileOffset: 10];

Для указания относительных позиций в файле нужно получитьтекущее сме-щение в файле и затем добавлять или вычитать из него относительное смещение. Например, чтобы пропустить следующие 128 байтов в файле, нужно написать следующее. [databaseHandle seekToFileOffet: [databaseHandle offsetlnFile] + 128];

Для смещения на пять целых данных в файле можно написать следующее. [databaseHandle seekToFileOffet: [databaseHandle offsetlnFile] – 5 * sizeof (int)];

Программа 16.8 добавляет содержимое одного файла ко второму. Второй файл открывается для записи, выполняется поиск конца этого файла и затем содержимое первого файла записывается во второй. // Добавление содержимого файла "fileA" в конец файла "fileB" #import #import #import #import #import #import int main (int argc, char *argv[]) { NSAutoreleasePool * poo) = [[NSAutoreleasePool alloc] init]; NSFileHandle *inFile, *outFile; NSData *buffer; // Открытие файла fileA для чтения inFile = [NSFileHandle fileHandleForReadingAtPatti: @"fileA*']; if (inFile == nil) { NSLog (@"Open of fileA for reading failed"); return 1; } // Открытие файла fileB для изменения outFile = [NSFileHandle fileHandleForWritingAtPath: @"fileB"]; if (outFile == nil) { NSLog (@"Open of fileB for writing failed"); return 2; } // Поиск конца outFile [outFile seekToEndOfFile]; // Чтение inFile и запись его содержимого в outFile buffer = [inFile readDataToEndOfFile]; [outFile writeData: buffer]; // Закрытие обоих файлов [inFile closeFile]; [outRle closeFile]; [pool drain]; return 0; }

Содержимое FileA перед запуском программы 16.8 This is line 1 in the first file. (Это строка 1 в первом файле) This is line 2 in the first file. (Это строка 2 в первом файле)

Содержимое FileB перед запуском программы 16.8 This is line 1 in the second file. (Это строка 1 во втором файле) This is line 2 in the second file. (Это строка 2 во втором файле)

Вывод программы 16.8 Contents of fileB (Содержимое fileB) This is line 1 in the second file. This is line 2 in the second file. This is line 1 in the first file. This is line 2 in the first file.

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

Внесите изменения в программу копирования (программа 16.6), чтобы она допускала более одного исходного файла для копирования в папку, аналогично стандартной команде UNIX ср. Например, команда $ copy copyl .m filel .т file2.m progs должна копировать три файла (copyl.m, filel.m и file2.m) в папку progs. Предусмотрите, что если указано более одного файла, то последним аргументом на самом деле является существующая папка.

Напишите средство командной строки с именем myfind, которое принимает два аргумента. Первый из них указывает начальную папку для поиска, а вто-рой– имя файла, который нужно найти. Например, командная строка $ myfind /Users proposai.doc /Users/stevekochan/MyDocuments/proposals/proposal.doc $ начинает поиск в файловой системе с /Users, чтобы найти файл proposal.doc. Выведите полный путь к файлу, если он найден (в том виде, как показано выше), или соответствующее сообщение, если он не найден.

Напишите вашу собственную версию стандартных средств UNIX basename и dirname.

Используя класс NSProcessInfo, напишите программу, которая выводит всю информацию, возвращаемую каждым из ее методов-получателей (getter).

Используя функцию NSTemporaryOirectory из NSPathUtilities.h и метод NSProcessInfo globallyUniqueString, описанные в этой главе, добавьте в NSString категорию с именем TempFiles и определите в ней метод с именем temporaryFileName, который возвращает при каждом вызове новое уникальное имя файла.

Внесите изменения в программу I6.7, чтобы в файле выполнялись чтение и запись kBufSize байт, причем kBufSize нужно определить в начале вашей программы. Обязательно проверьте эту профамму на больших файлах (размером больше kBufSize байт).

Откройте файл, считывайте его содержимое по 128 байт и выводите эти данные на терминал. Используйте метод FileHandle fileHandleWithSlandardOutput, чтобы получить описатель для вывода на терминал.

Глава 17. Управление памятью

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

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

В этой главе мы обсудим стратегию выделения памяти в Foundation, пул ав-томатического высвобождения (autorelease pool) и идею «удержания» (retain) объектов. Вы узнаете также о счетчике ссылок (reference count) объекта и о сборке мусора (garbage collection), которая облегчает задачу удержания и последующего высвобождения объектов. Однако, как вы увидите, сборку мусора нельзя ис-пользовать для приложений iPhone, поэтому вы должны знать способы управления памятью, которые описываются в этой книге. 17.1. Автоматически высвобождаемый пул

Вы уже знакомы с автоматически высвобождаемым пулом (пулом автомати-ческого освобождения памяти) из примеров во второй части книги. При работе с программами Foundation вы должны создавать этот пул для работы с объектами Foundation. Именно в этом пуле программа следит за объектами для их даль-нейшего высвобождения. Как уже говорилось, пул в приложении можно задать с помощью следующего вызова. NSAutoreleasePool * pool = [ [ N SAutor el ease Poo I alloc] init];

После создания этого пула Foundation автоматически добавляет н него оп-ределенные массивы, строки, словари и другие объекты. Закончив использование пула, можно освободить память, которую он использует, отправив сообщение drain. [pool drain];

Автоматически высвобождаемый пул называется так потому, что любые объекты, которые помечаются как автоматически высвобождаемые (autorelease) и поэтому добавляются в этот пул, автоматически высвобождаются, когда выс-вобождается сам пул. В программе можно иметь несколько autorelease-пулов, и они могут быть также вложенными.

Если программа создает много временных объектов (что часто происходит при выполнении кода в цикле), может потребоваться создание нескольких autorelease-пулов. Например, в следующем фрагменте кода показано, как со-здавать autorelease-пулы для высвобождения временных объектов, создаваемых на каждом шаге цикла for. NSAutoreleasePool *tempPool; for (i = 0; i < n; ++i) { tempPool = [[NSAutoReleasePool alloc] init]; ... // здесь выполняется много работы с временными объектами [tempPool drain]; }

Отметим, что при опустошении пула (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 #import < Foundation/NSAutoreleasePool.h> #import #import #import 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 #import Simport #import 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-файле .)

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

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


    Ваша оценка произведения:

Популярные книги за неделю