Программирование на Objective-C 2.0
Шрифт:
Эта программа создает адресную книгу и затем архивирует ее в файле addrbook.arch. При создании архивного файла метод кодирования вызываются из обоих классов: AddressBook и AddressCard. Если вы хотите проверить это, добавьте в методы несколько вызовов NSLog.
В программе 19.7 показано, как считывать архив в память для создания ад-ресной книги из файла. #import "AddressBook.h" #import <Foundation/NSAutoreleasePool.h> int main (int argc, char *argv[]) { AddressBook *myBook; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; myBook = [NSKeyedUnarchiver unarchiveObjectWithFile: (@"addrbook.arch"]; [myBook list]; [pool drain]; return 0; }
Вывод программы 19.7 ======== Contents of: Steve’s Address Book == Jamie Baker jbaker@hitmail.com Julia Kochan jewls337@axlc.com Stephen Kochan steve@steve_kochan.com Tony lannino tony.iannino@techfitness.com
При разархивации этой адресной книги автоматически вызываются методы декодирования, добавленные
Метод encodeObjectforKey: используется применительно к встроенным классам и классам, для которых вы пишете свои методы кодирования и декодирования согласно протоколу NSCoding. Если ваши переменные экземпляра содержат некоторые базовые типы данных, например, int или float, вы должны знать, как кодировать и декодировать их (см. таблицу 19.1).
Ниже приводится простое определение для класса с именем Foo, который содержит три переменные экземпляра: типа NSString, типа int и типа float. Этот класс содержит один метод-установщик, три метода-получателя и два метода кодирования/декодирования, используемых для архивации. @interface Foo: NSObject <NSCoding> { NSString *strVal; int intVal; float floatVal; } @property (copy, nonatomic) NSString *strVal; @property int intVal; @property float floatVal; @end
Затем следует файл секции implementation. @implementation Foo @synthesize strVal, intVal, floatVal; -(void) encodeWithCoder: (NSCoder *) encoder { [encoder encodeObject: strVal forKey: @"FoostrVarj; [encoder encodelnt: intVal forKey: @"FoointVal"]; [encoder encodeFloat: floatVal forKey: @"Foofk>atVar]; } -(id) initWithCoder: (NSCoder *) decoder { strVal = [[decoder decodeObjectForKey: @"FoostrVal"] retain]; intVal = [decoder decodelntForKey: @"FoointVar]; floatVal = [decoder decodeFloatForKey: @'FoofloatVal"]; return self; } @end
В этой процедуре кодирования сначала кодируется строковое значение strVal с помощью метода encodeObject:forKey:, как показано ранее.
В программе 19.8 создается объект Foo, выполняется его архивация в файл, разархивация и последующий вывод. #import <Foundation/NSObject.h> #import <Foundation/NSString.h> #import <Foundation/NSKeyedArchiver.h> #import <Foundation/NSAutoreleasePool.h> #import "Foo.h" // Definition for our Foo class int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Foo *myFoo1 = [[Foo alloc] init]; Foo *myFoo2; [myFool setStrVal: @»This is the string»]; [myFool setlntVal: 12345]; [myFool setFloatVal: 98.6]; [NSKeyedArchiver archiveRootObject: myFool toFile: @"foo.arch"]; myFoo2 = [NSKeyedUnarchiver unarchiveObjectWithFile: @"foo.arch"]; NSLog (@"%@\n%i\n%g", [myFoo2 strVal], [myFoo2 intVal], [myFoo2 floatVal]); [myFool release]; [pool drain]; return 0; }
Вывод программы 19.8 This is the string (Это строка) 12345 98.6
Архивация трех пробных экземпляра из объекта выполняется с помощью следующих сообщений. [encoder encodeObject: strVal forKey: @"FoostrVar]; [encoder encodelnt: intVal forKey: @"FoointVal“]; [encoder encodeRoat: floatVal forKey: @"FoofloatVa!''];
Некоторые из базовых типов данных, такие как char, short, long и long long, не включены в таблицу 19.1; для них необходимо определить размер объекта дан-ных и использовать соответствующую процедуру. Например, тип short int обыч-но имеет размер 16 битов, int и long — 32 или 64 бита, и long long — 64 бита. (Размер любого типа данных определяется с помощью оператора sizeof, см. главу 13.) Например, данные типа short int нужно сохранить их сначала как тип int и затем архивировать с помощью encodeintforKey:. Для декодирования нужно использовать обратный процесс: применить decodelnt:forKey: и затем присвоить результат переменной типа short int. 19.4. Использование NSData для создания нестандартных архивов
Возможно, вы не хотите записывать объект непосредственно в файл с помощью метода archiveRootObject:ToFile:, как в предыдущих примерах. Например, вам нужно собрать некоторые объекты и сохранить их в одном архивном файле. Это можно сделать с помощью обобщенного класса объектов потока данных (data stream) NSData.
Как говорилось в главе 16, объект класса NSData можно использовать для ре-зервирования области памяти для сохранения данных. Эту область памяти можно использовать, например, для временного хранения данных, которые будут последовательно записываться в файл, или для хранения содержимого файла, считанного с диска. Проще всего создать мутабельную область данных с помощью метода data. dataArea = [NSMutableData data];
В результате создается пустое буферное пространство, размер которого расширяется по мере выполнения программы.
В качестве простого примера предположим, что нужно архивировать в од-ном файле адресную книгу и один из объектов класса Foo. Предположим также, что мы добавили методы архивации с ключами в классы AddressBook и AddressCard (см. программу 19.9). #import <Foundation/NSObject.h> #import <Foundation/NSAutoreleasePool.h> #import <Foundation/NSString.h> #import < Foundation/NSKeyedArchiver. h> #import <Foundation/NSCoder.h> #import <Foundation/NSData.h> #import "AddressBook.h" #import Too.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Foo *myFoo1 = [[Foo alloc] init]; Foo *myFoo2; NSMutableData *dataArea; NSKeyedArchiver *archiver; AddressBook *myBook; //
Вставьте здесь код из программы 19.7 для создания в myBook // адресной книги, содержащей четыре адресные карточки [myFool setStrVal: @"This is the string"]; [myFool setlntVal: 12345]; [myFool setFloatVal: 98.6]; // Создание области данных и ее присоединение к объекту NSKeyedArchiver dataArea = [NSMutableData data]; archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: dataArea]; // Теперь можно начать архивацию объектов [archiver encodeObject: myBook forKey: @"myaddrbook"]; [archiver encodeObject: myFool forKey: @"myfoor]; [archiver finishEncoding]; // Запись архивируемой области данных в файл if ([dataArea writeToFile: @"myArchive" atomically: YES encoding: NSUTF8Encoding error: nil] == NC) NSLog (@"Archiving failed!"); (Архивация на выполнена) [archiver release]; [myFool release]; [pool drain]; return 0; }После выделения памяти для объекта NSKeyedArchiver передается сообщение initForWritingWithMutableData:, чтобы указать область, в которую будут записываться архивируемые данные; это область NSMutabledata с именем dataArea, которая была создана выше. Сохраненному в архиваторе объекту NSKeyedArchiver можно передавать теперь сообщения кодирования для архивации объектов в программе. На самом деле, архивация и сохранение все кодируемых сообщений выполняется в указанной области данных, пока не получено сообщение finishEncoding.
В данном случае кодируются два объекта: наша адресная книга и объект класса Foo. Для этих объектов можно использовать encodeObjectiforKey:, поскольку мы ранее реализовали методы кодирования (encoder) и кодирования (decoder) для классов AddressBook, AddressCard и Foo.
Закончив архивацию этих объектов, мы передаем объекту archiver сообще-ние finishEncoding. После этого нельзя кодировать никакие объекты, и мы должны передать это сообщение, чтобы завершить процесс архивации.
Область с именем dataArea теперь содержит наши архивированные объекты в форме, которую можно записать в файл. В выражении с сообщением [dataArea writeToFile: @"myArchive" atomically: YES encoding: NSUTF8Encoding error: nil]
передается сообщение writeToFtle:atomically:encoding:error: потоку данных для записи его данных в указанный файл с именем myArchive.
Как видно из части с оператором if, метод writeToFi(e:atomicalfy:encoding:error: возвращает значение YES типа BOOL, если операция записи успешно выполнена, и значение N0, если ее не удалось выполнить (например, был указан неверный путь к файлу или переполнена файловая система).
Восстановление данных из архивного файла осуществляется просто: нужно выполнить все действия в обратном порядке. Во-первых, нужно выделить, как и раньше, область данных, затем в эту область данных прочитать архивный файл. После этого мы создаем объект NSKeyedUnarchiver и сообщаем ему, что требуется декодировать данные из указанной области. Для извлечения и декодирования архивированных объектов нужно вызвать методы декодирования, а по окончании - передать сообщение finishDecoding объекту NSKeyedUnarchiver. Все это выполняется в программе 19. К). #import <Foundation/NSObject.h> #import <Foundation/NSAutoreleasePool.h> #import <Foundation/NSString.h> #import <Foundation/NSKeyedArchiver.h> #import <Foundation/NSCoder.h> #import <Foundation/NSData.h> #import "AddressBook.h" #import "Foo.h" * pool = [[NSAutoreleasePool alloc] inrt]; *dataArea; *unarchiver; *myFoo1; *myBook; // Чтение архива и присоединение к нему // объекта NSKeyedUnarchiver dataArea = [NSData dataWithContentsOfFile: @"myArchive"]; if (! dataArea) { NSLog (@"Can’t read back archive file!"); Return (1); } unarchiver = [[NSKeyedllnarchiver alloc] initForReadingWithData: dataArea]; // Декодирования объектов, которые мы сохранили ранее в этом архиве myBook = [unarchiver decodeObjectForKey: @nmyaddrbook"]; myFool = [unarchiver decodeObjectForKey: @"myfoo1"]; [unarchiver finishDecoding]; [unarchiver release]; // Проверка того, что восстановление успешно выполнено [myBook list]; NSLog ("%@\n%i\n%g", [myFool strVal], [myFool intVal], [myFool floatVal]); [pool release]; return 0; }
Вывод программы 19.10 ======== Contents of: Steve's Address Book — Jamie Baker jbaker@hitmail.com Julia Kochan jewls337@axlc.com Stephen Kochan steve@steve_kochan.com Tony lannino tony.iannino@techfitness.com This is the string 12345 98.6
Адресная книга и объект Foo были успешно восстановлены из архивного файла. 19.5. Использование архиватора для копирования объектов
В программе 18.2 мы пытались создать копию массива, содержащего мутабель- ные строковые элементы, и создали поверхностную (shallow) копию этого мас-сива — копировались не сами строки, а только ссылки на них.