Текст книги "Программирование на Objective-C 2.0"
Автор книги: Стивен Кочан
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 21 (всего у книги 32 страниц)
После завершения упражнения 3 внесите изменения в метод lookup: из уп-ражнения 2, чтобы выполнять поиск по всем полям адресной карточки. Как вы спроектировал и бы свои классы AddressCard и AddressBook, чтобы в AddressBook не нужно было знать все поля, хранящиеся в AddressCard?
Добавьте метод removeName: в класс AddressBook, чтобы удалять запись адресной книги в соответствии со следующим объявлением этого метода. -(BOOL) removeName: (NSString *) theName; Используйте метод lookup:, разработанный в упражнении 2. Если имя не най-дено или существует несколько записей, метод должен возвращать значение N0. Если запись успешно удалена, метод должен возвращать значение YES.
Используя класс Fraction, определенный в части 1, создайте массив дробей (fraction) с некоторыми произвольными значениями. Затем напишите код для вычисления суммы всех дробей, хранящихся в этом массиве.
Используя класс Fraction, определенный в части I, создайте мутабельный мас-сив дробей (fraction) с произвольными значениями. Затем отсортируйте этот массив с помощью метода sortUsingSelector: из класса NSMutableArray. Добавьте в класс Fraction категорию Comparison (сравнение) и реализуйте свой метод сравнения в этой категории.
Определите три новых класса с именами Song, PlayList и MusicCollection. Объект класса Song должен содержать информацию об определенной песне, например, ее название (title), исполнителя (artist), альбом (album) и время воспроизведения (playing time). Объект класса PlayList должен содержать имя списка воспроизведения и коллекцию песен. Объект класса MusicCollection должен содержать коллекцию списков воспроизведения, включая специальный мастер-список с именем library (библиотека), который содержит все песни этой коллекции. Определите эти три класса и напишите методы, чтобы выполнять следующее.
Создавать объект класса Song и задавать его информацию.
Создавать объект класса Playlist и добавлять или удалять песни из списка воспроизведения. Новая песня должна добавляться в мастер-список, если ее еще там нет. При удалении песни из мастер-списка она должна удаляться из всех списков воспроизведения этой музыкальной коллекции.
Создавать объект класса MusicCollection и добавлять в него или удалять из него объекты класса Playlist (списки воспроизведения)
Выполнять поиск и вывод информации о любой песне, любом списке воспроизведения или всей музыкальной коллекции. Проследите, чтобы во всех классах не было утечки памяти!
Напишите программу, которая создает из массива объектов типа NSInteger гистограмму, где показано каждое целое значение вместе с числом экземп-ляров этого значения (частотой появления) в массиве. Для создания счетчиков экземпляров используйте объект NSCountedSet.
Глава 16. Работа с файлами
Foundation framework позволяет получать доступ к файловой системе для вы-полнения основных операций с файлами и папками (каталогами) с помощью NSFileManager, методы которого позволяют выполнять следующие операции.
Создание нового файла.
Чтение из существующего файла.
Запись данных в файл.
Переименование файла.
Удаление файла.
Проверка существования файла.
Определение размера файла, а также других атрибутов.
Создание копии файла.
Проверка двух файлов на совпадение содержимого.
Многие из этих операций можно также выполнять с папками. Например, можно создать папку, прочитать ее содержимое или удалить се. Еще одна возможность – это возможность привязки (link) файлов. Привязка означает, что один и тот же файл может существовать под двумя именами и даже в двух различных папках.
Чтобы открыть файл и выполнить с этим файлом несколько операций чте-ния-записи, используются методы из NSFileHandle. Методы этого класса позво-ляют следующее.
Открывать файл для чтения, записи или изменения (update – чтение и запись).
Искать указанное местоположение в файле.
Считывать или записывать заданное число байтов из файла или в файл. Методы из NSFileHandle можно также применять к устройствами (сокетам). В этой главе мы будем работать только с обычными файлами. 16.1. Управление файлами и папками: NSFileManager
Файл или папка уникально указывается для NSFileManager с помощью имени пути доступа к файлу (pathname). Имя пути – это объект класса NSString, который может представлять относительное или полное имя пути. Относительное имя пути определяется относительно текущей папки. Например, имя файла copyl.m означает, что файл copyl.m находится в текущей папке. Символы «слэш» являются разделителями папок в указанном пути. Имя файла ch16/copy1.m тоже является относительным именем пути, указывая файл copyl .m, хранящийся в папке chi6, которая содержится в текущей папке.
Полные имена пути, которые также называют абсолютными именами пути, начинаются с ведущего слэша (/). Слэш на самом деле представляет папку, ко-торая называется корневой (root) папкой. На моем Мае полное имя пути к моей домашней папке – /Users/stevekochan. Этот путь представляет три папки: / (корне-вая папка), Users и stevekochan.
Специальный символ «тильда» (~) используется как сокращенное представ-ление домашней папки пользователя. Так, -linda – это сокращение для домашней папки пользователя linda, которая может быть представлена в виде пути / Users/linda. Отдельный символ «тильда» указывает домашнюю папку текущего пользователя, путь -/copyl.m означает ссылку на файл copyl.m, хранящийся в до-машней папке текущего пользователя. Другие специальные символы для пути в стиле UNIX, такие как точка (.) для текущей папки и .. для родительской папки, следует удалять из имен пути при работе с файлами в методах Foundation. Для этого можно использовать разнообразные утилиты, которые будут рассмат-риваться ниже в этой главе.
Избегайте фиксированных путей в своих программах. Как будет описано далее, имеются функции и методы, которые позволяют получать имя пути для текущей папки, домашней папки пользователя и папки для создания временных файлов. Обращайтесь к ним, насколько это возможно. Ниже мы покажем, что Foundation содержит функцию для получения списка специальных папок, таких как папки пользователя Documents.
В таблице 16.1 приводится сводка основных методов NSFileManager для работы с файлами. В этой таблице path (путь), path}, palh2,from (из) и !о (куда) – объекты класса NSString, attr (атрибут) – объект NSDictionary, handler – обработчик (хендлср) обратного вызова, который вы можете предоставлять для обработки ошибок. Если указать nil для handler, то выполняется действие по умолчанию. Для методов, возвращающих значение типа BOOL, это YES при успешном завершении операции и N0, если операцию не удалось выполнить. В этой главе не товорится о том, как писать хендлеры.
Табл. 16.1. Наиболее распространенные файловые методы класса NSFileManager Метод Описание -(NSData *) contentsAtPath: path Читает данные из файла. -(BOOL) createFileAtPath: path contents: (BOOL) data attributes: attr Пишет данные (data) в файл -(BOOL) removeFileAtPath: path handler: handler Удаляет файл. -(BOOL) movePath: from toPath: to handler: handler Переименовывает или перемещает файл (to не может существовать заранее). -(BOOL) copyPath: from toPath: to handler: handler Копирует файл (to не может существовать заранее). -(BOOL) contentsEqualAtPath: path1 andPath: path2 Сравнивает содержимое двух файлов. -(BOOL) fileExistsAtPath: path Проверяет существование файла. -(BOOL) isReadableRleAtPath: path Проверяет, существует ли файл и доступен ли он для чтения. -(BOOL) isWritableFileAtPath: path Проверяет, существует ли файл и доступен ли он для записи. -(NSOictionary *) fileAttributesAtPath: path traverseLink: (BOOL) flag Читает атрибуты файла. -(BOOL) changeFileAttributes: attr atPath: path Изменяет атрибуты
Каждый из этих файловых методов вызывается в объекте NSFileManager, который создается при отправке сообщения defauttManager этому классу. NSFileManager *fm; ... fm = [NSFileManager defaultManager];
Например, для удаления файла todolist из текущей папки нужно создать сна-чала объект класса NSFileManager, как показано выше, и затем вызвать метод removeFileAtPath:. [fm removeFileAtPath: @"todolist" handler: nil];
Возвращаемый результат можно проверить, чтобы убедиться, что удаление этого файла выполнено успешно. if ([fm removeFileAtPath: @"todolistn handler: nil] == NO) { NSLog (@"Couldn’t remove file todolist"); (Нельзя удалить файл todolist) return 1; }
Словарь атрибутов позволяет, в частности, указывать разрешения доступа к создаваемому файлу или получать либо изменять информацию для существую-щего файла. Разрешения по умолчанию задаются при создании файла с указанием значения nil в качестве этого параметра. Метод fileAttributesAtPadiitraverseUnk: возвращает словарь, содержащий атрибуты указанного файла. Параметр traverseLink: имеет значение YES или N0 для символических ссылок. Если файл задан символической ссылкой и указано значение YES, то возвращаются атрибуты файла привязки; если указано значение N0, то возвращаются атрибуты самой привязки (ссылки).
Для уже существующих файлов в словарь атрибутов включается такая ин-формация, как владелец файла, размер файла, дата его создания, и т.д. Каждый атрибут можно извлекать из словаря по его ключу; все эти ключи определены в . Например, NSFileSize – это ключ для атрибута размера файла.
В программе 16.1 показаны основные операции с файлами. В этом примере предполагается, что в текущей папке есть файл testfile, содержащий следующие три строки текста. This is a test file with some data in it. (Это тестовый файл с некоторыми данными.) Here’s another line of data. (Это еще одна строка данных.) And a third. (И третья.) // Основные файловые операции // Предполагается, что существует файл "testfile" // в текущей рабочей папке #import
Вывод программы 16.1 File size is 84 bytes (Размер файла 84 байта) All operations were successful! (Все операции выполнены успешно) This is a test file with some data in it. (Это тестовый файл с некоторыми данными.) Here's another line of data. (Это еще одна строка данных.) And a third. (И третья.)
Программа сначала проверяет, существует ли файл testfile. Если да, то про-грамма создает его копию и затем проверяет эти файлы на совпадение. Опытные пользователи UNIX обратят внимание, что мы не можем переместить или копировать файл в определенную папку, просто указав эту целевую папку для методов copy Path: toPath: и movePath:toPath:; в этой папке должно быть явно указано имя файла.
Примечание. Мы можем создать testfile с помощью Xcode, выбрав New File... (создать файл) в меню File. В появившейся левой панели нужно выделить Other (Другое) и затем выбрать в правой панели Empty File (Пустой файл). Введите testfile как имя файла и убедитесь, что он создается в той же папке, что и выпол-няемый файл – в вашей папке проекта Build/Debug.
Метод movePath:toPath: можно использовать для перемещения файла из одной папки в другую. (Или для перемещения целой папки.) Если оба пути указывают на одну и ту же папку (как в нашем примере), то результатом будет переименование файла. Например, в программе 16.1 мы переименовываем файл newfile в newfile2.
Как указывалось в таблице 16.1, при выполнении операций копирования, переименования или перемещения указанный целевой файл (to) не может су-ществовать заранее, иначе операция не будет выполнена.
Размер файла newfile2 определяется с помощью метода fileAttributesAtPath:traverseUnk:. Мы проверяем, что возвращается непустой (не nil) словарь, и затем используем метод NSDictionary objectForKey: для получения из словаря размера файла с помощью ключа NSFileSize. Затем выводится целое значение из словаря.
Метод removeFileAtPath:handler: удаляет наш исходный файл testfile.
И, наконец, метод NSString stringWithContentsOfFile: читает содержимое файла newfile2 в строковый объект, который затем передается как аргумент в NSLog для вывода.
В программе 16.1 проверяется успешность выполнения каждой файловой операции. Если операция не выполнена, выводится сообщение об ошибке с помощью NSLog, и программа завершает работу, возвращая ненулевое значение состояния выхода. Каждое ненулевое значение, которое соответствует ошибке в программе, уникальным образом определяет тип ошибки. Если вы пишете средства, запускаемые из командной строки, это полезный способ, поскольку другая программа может проверять возвращаемое значение, например, из сце-нария оболочки. Работа с классом NSData
При работе с файлами часто требуется читать данные во временную область хранения в памяти, которую называют буфером (buffer). Буфер часто использу-ется при сборе данных для последующего вывода в файл. Класс Foundation NSData позволяет легко создавать буфер, читать в него содержимое файла или писать содержимое буфера в файл. Для 32-битного приложения в буфере NSDATA можно хранить до 2 Гб. В случае 64-битного приложения в таком буфере можно хранить до 8 Эб (экзабайт), то есть 8000 Гб информации!
Можно определять немутабельные (NSData) или мутабельные (NSMutableData) области памяти. Мы ознакомим вас с методами данного класса в этой главе, а также в последующих главах.
В программе 16.2 показано, как читать содержимое файла в буфер, опреде-ленный в памяти.
Эта программа читает содержимое файла newftle2 и записывает его в новый файл с именем newfile3. В некотором смысле это реализация операции копиро-вания файла, хотя и не столь простая, как метод copy Rath :to Path: handler:, // Создание копии файла #import
Вывод программы 16.2 File copy was successful! (Копирование файла выполнено успешно)
Метод NSData contentsAtPath: просто принимает имя пути и читает содержимое указанного файла в область памяти (которую он создает). Метод возвращает в качестве результата объект области памяти или nil, если операцию чтения не удается выполнить (например, если этот файл не существует или недоступен для чтения).
Метод createRleAtPath:соntents:attributes: создает файл с указанными атрибутами (или использует атрибуты по умолчанию, если для аргумента атрибутов указано значение nil). Затем содержимое указанного объекта NSData записывается в файл. В нашем примере эта область памяти содержит данные прочитанного ранее файла. Работа с папками
В таблице 16.2 приводятся методы NSFileManager для работы с папками (катало-гами). Многие из этих методов аналогичны методам для обычных файлов из таблицы 16.1 (обозначения такие же, как в таблице 16.1).
Табл. 16.2. Наиболее распространенные методы Метод Описание -(NSString *) currentDirectoryPath Получает текущую папку. -(BOOL) changeCurrentDirectoryPath: path Изменяет текущую папку. -(BOOL) copyPath: from toPath: to handler: handler Копирует структуру папки (to не может существовать заранее). -(BOOL) createDirectoryAtPath: path attributes: attr Создает новую папку. -(BOOL) fileExistsAtPath: path isDirectory: (BOOL *) flag Проверяет, содержится ли данный файл в папке (результат YES/NO сохраняется в переменной flag). -(NSArray *) directoryContentsAtPath: path Создает список содержимого папки. -(NSDirectoryEnumerator *) enumeratorAtPath: path Перечисляет содержимое папки. -(BOOL) removeFileAtPath: path handler: handler Удаляет пустую папку -(BOOL) movePath: from toPath: to handler: handler Переименовывает или перемещает папку (to не может существовать заранее).
В программе 16.3 показаны основные операции с папками. // Основные операции с папками #import
Вывод программы 16.3 Current directory path is /Users/stevekochan/progs/ch16 (Путь к текущей папке) Current directory path is /Users/stevekochan/progs/ch16/newdir All operations were successful! (Все операции выполнены успешно)
Работу программы 16.3 легко понять из текста самой программы. Сначала мы получаем путь к текущей папке для информативных целей. Затем в текущей папке создается новая папка testdir. Затем в программе используется метод movePath:toPath:handler: для переименования этой новой папки из testdir в newdir. Помните, что этот метод позволяет также перемещать всю структуру папки (включая ее содержимое) из одного места файловой системы в другое.
После переименования новой папки программа делает эту новую папку те-кущей с помощью метода changeCurrentDirectoryPath:. Затем выводится путь к текущей папке, чтобы убедиться, что изменение было выполнено успешно. Перечисление содержимого папки
Получил! список содержимого папки. Этот процесс можно осуществить с по-мощью метода enumeratorAtPath: или directoryContentsAtPath:. В первом случае каждый файл указанной папки перечисляется по отдельности, Если один из этих файлов является папкой, то по умолчанию его содержимое тоже рекурсивно перечисляется. Во время этого процесса мы можем динамически запрещать рекурсию (отправив сообщение skipDescendants объекту перечисления), чтобы содержимое папки не перечислялось.
В случае метода directoryContentsAtPath: выполняется перечисление указанной папки, и метод возвращает массив со списком. Если какой-либо из этих файлов является папкой, его содержимое не перечисляется данным методом. В программе 16.4 показано, как использовать каждый из этих методов. // Перечисление содержимого папки #import
Вывод программы 16.4 Contents of /Users/stevekochan/mysrc/ch16: (Содержимое папки) a.out dirl.m dir2.m filel .m newdir newdir/filel.m newdir/output pathl.m testfile Contents using directoryContentsAtPath: (Содержимое с помощью directoryContentsAtPath:) a.out dirl.m dir2.m filel.m newdir pathl.m testfile
Рассмотрим следующую последовательность кода. dirEnum = [fm enumeratorAtPath: path]; NSLog (@"Contents of path); while ((path = [dirEnum nextObject]) != nil) NSLog (@"%@", path);
Мы начинаем перечисление содержимого папки с отправки сообщения enumeratioaAtPatti: объекту filemanager, в данном случае – fm. Метод enumeratorAtPath: возвращает объект NSDirectortyEnumerator, который сохраняется в dirEnum. Теперь каждый раз при отправке сообщения nextObject этому объекту метод возвращает путь к следующему файлу в перечисляемой папке. Когда не остается файлов для перечисления, метод возвращает значение nil.
Из вывода программы 16.4 видно, чем отличаются эти методы. Метод enumeratorAtPath: создает список содержимого папки newdir, а метод directoryContentsAtPath: не создает. Если бы папка newdir содержала подпапки, то они тоже были бы перечислены методом enumeratorAtPath:.
Как говорилось выше, при выполнении цикла while в программе 16.4 вы могли бы запретить перечисление содержимого любых подпапок, внеся в код следующее изменение. while ((path = [dirEnum nextObject]) != nil) { NSLog (@"%@", path); [fm fileExistsAtPath: path isDirectory: &flag]; if (flag == YES) [dirEnum skipDescendents]; }
Здесь флаг представлен переменной типа BOOL. Метод fileExistsAtPath: сохраняет значение YES в этом флаге, если указанный путь содержится в папке, иначе сохраняется значение NO.
Вы можете вывести все содержимое dirArray с помощью одного вызова NSLog NSLog (@"%@", dirArray);
вместо быстрого перечисления, использованного в программе. Работа с путями: NSPathUtilities.h
В NSPathUtilities.h включены расширения функций и категорий к NSString для ра-боты с путями. Они позволяют сделать ваши программы более независимыми от структуры файловой системы и местонахождения конкретных файлов и папок. В программе 16.5 показано, как работать с функциями и методами из NS Path Utilities.h. // Основные операции с путями #import
Вывод программы 16.5 Temporary Directory is /var/folders/HT/HTyGLvSNHTuNb6NrMuo7QE+++TI/-Tmp-/ (Временная папка) Base dir is examples (Базовя папка – examples) fullpath lo path.m is /Users/stevekochan/progs/examples/path.m (полный путь к ...) extension for /Users/stevekochan/progs/examples/path.m is m (расширение имени для ...) Your home directory is /Users/stevekochan (Ваша домашняя папка ...) / Users stevekochan -stevekochan/progs/../ch16/./path.m => /Users/stevekochan/chl 6/path.m
Функция NSTemporaryDirectory возвращает имя пути в системе для папки, в которой могут сохраняться временные файлы. Создавая временные файлы в этой папке, не забывайте удалять их по окончании использования. Кроме того, имена файлов должны быть уникальными, особенно в тех случаях, когда несколько экземпляров вашего приложения работает одновременно. (См. упражнение 5 в конце этой главы.) Это может произойти, если несколько пользователей выполнят вход в вашу систему и будут выполнять одно приложение.
Метод lastPathComponent извлекает последний файл, указанный в пути. Это полезно, если нужно получить из абсолютного имени пути только базовое имя файла.
Метод stringВуАрреndingPathComponent: позволяет присоединять имя файла в конце пути. Если имя пути, указанное как получатель, не заканчивается символом «слэш», метод вставляет этот символ в имя пути, чтобы отделить его от присоединяемого имени файла. Сочетая метод currentDirectory с методом stringByAppendingPathComponent:, можно создавать полный путь к файлу в текущей папке. Этот способ показан в программе 16.5.
Метод pathExtension дает расширение имени файла для указанного имени пути. Например, расширением для имени файла path.m является т, и данный метод возвращает это расширение. Если имя файла не содержит расширения, то метод просто возвращает пустую строку.
Функция NSHomeDirectory возвращает домашнюю папку для текущего пользователя. Чтобы получить домашнюю папку для определенного пользователя, можно использовать вместо этого функцию NSHomeDirectoryForUser, указав имя пользователя как аргумент для этой функции.
Метод pathComponents возвращает массив, содержащий каждый из компонентов указанного пути. В программе 16.5 последовательно извлекается каждый элемент возвращаемого массива, и каждый компонент отображается в отдельной строке вывода.
И, наконец, иногда имена пути содержат символы «тильда» (~), о чем мы уже говорили выше. Методы FileManager воспринимают – как сокращенное пред-ставление домашней папки пользователя, или -user для домашней папки ука-занного пользователя. Если ваши имена путей могут содержать символы «тиль-да», то раскрыть их позволяет метод stringByStandardizingPath. Этот метод возвращает путь в стандартизованном виде, то есть с удалением специальных символов. Метод stringByExpandingTildelnPatii раскрывает тол ько символ «тильда», если он присутствует в имени пути. Наиболее распространенные методы для работы с путями
В таблице 16.3 приводятся наиболее распространенные методы для работы с путями. В данной таблице components – это объект NSArray, содержащий строко-вые объекты для каждого компонента пути; path – это строковый объект, ука-зывающий путь к файлу; ext – это строковый объект, указывающий расширение имени файла (например, @"mp4"). Метод Описание +(NSStrirtg *) pathWittiComponents: components Создает допустимый путь из элементов components. -(NSArray *) pathComponents Разделяет путь на его составляющие компоненты. -(NSString *) lastPathComponent Извлекает последний компонент пути. -(NSString *) path Extension Извлекает расширение имени из последнего компонента пути. -(NSString *) stringByAppendingPathComponent: path Добавляет путь path в конец существующего пути. -(NSString *) stringByAppendingPathExtension: ext Добавляет расширение имени ext к последнему компоненту пути. -(NSString *) string ByDeletingLastPathComponent Удаляет последний компонент пути. -(NSString *) string ByDeleting LastPathComponent Удаляет последний компонент пути. -(NSString *) stringByDeletingPathExtension Удаляет расширение имени из последнего компонента пути. -(NSString *) stringByExpandingTildelnPath Раскрывает символы «тильда» в пути как домашнюю папку текущего пользователя (;) или домашнюю папку указанного пользователя (@@-user). -(NSString *) stringByResolvingSymlinksInPath Пытается разрешить (раскрыть) символические ссылки в пути. -(NSString *) stringByStandardizingPath Стандартизует путь, раскрывая – ..(родительская папка), .(текущая папка) и символические ссылки.
В таблице 16.4 представлены имеющиеся функции для получения информа-ции о пользователе, его домашней папке и его панке для хранения временных файлов.
Табл. 16.4. Наиболее распространенные функции для работы с путями Функция Описание NSString *NSUserName (void) Возвращает имя входа (login) текущего пользователя. NSString *NSFullUserName (void) Возвращает полное пользовательское имя текущего пользователя. NSString *NSHomeDirectory (void) Возвращает путь к домашней папке текущего пользователя. NSString NSHomeDirectoryForUser (NSString user) Возвращает домашнюю папку пользователя user. NSString *NSTemporaryDirectory(void) Возвращает путь к папке, которую можно использовать для создания временного файла.