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

ЖАНРЫ

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

Вывод программы 19.4 abstract class: A class defined so other classes can inherit from it. adopt: To implement all the methods defined in a protocol archiving: Storing an object for later use.

Оператор glossary = [NSKeyedUnarchiver unarchiveObjectWithFile: @"glossary.archive"];

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

После восстановления словаря программа просто выполняет перебор его содержимого, чтобы убедиться, что восстановление было успешным. 19.3. Написание методов кодирования и декодирования

Объекты базовых классов Objective-C, таких как NSString, NSArray, NSDictionary, NSSet, NSDate, NSNumber и NSData, можно архивировать и восстанавливать,

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

Мы не можем непосредственно архивировать нашу адресную книгу (AddressBook) с помощью этих средств, поскольку система Objective-C «не знает», как архивировать объект класса AddressBook. Если попытаться архивировать его и программе с помощью строки [N S Keyed Arc h iver archiveRootObject: myAddressBook toFile: @naddrbook.arch"];

то при выполнении программы будет выведено сообщение *** -[AddressBook encodeWithCoder:]: selector not recognized (селектор не распознан) *** Uncaught exception: <NSInvalidArgumentException> (Невыявленное исключение) ***-[AddressBook encodeWithCoder:]: selector not recognized archiveTest: received signal: Trace/BPT trap

Из этих сообщений об ошибках мы видим, что система пыталась найти метод с именем encodeWithCoder: в классе AddressBook, но мы нигде не определяли такой метод.

Чтобы архивировать объекты, отличные от списка в начале раздела, мы должны указать системе, как эти объекты архивировать, или кодировать (encode), а также как их разархивировать, или декодировать (decode). Для этого нужно добавить в определения класса методы encodeWithCoder: и initWithCoder: согласно протоколу . В примере с адресной книгой нужно добавить эти методы в два класса: AddressBook и AddressCard.

Метод encodeWithCoder: вызывается каждый раз, когда архиватору нужно ко-дировать объект из указанного класса, и этот метод указывает ему, как это сделать. Аналогичным образом, метод initWithCoder: вызывается каждый раз, когда нужно декодировать объект указанного класса.

В общем случае метод кодирования должен указывать, как архивировать каждую переменную экземпляра в объекте, который нужно сохранить. Для этого есть вспомогательные средства. Для описанных выше базовых классов Objective- С можно использовать метод encodeObject:forKey:. Для базовых типов данных С (например, делых и с плавающей точкой) используется один из методов, при-веденных в таблице 19.1. Метод декодирования (decoder), initWithCoder:, действует в обратном порядке: мы используем decodeObject:forKey: для декодирования ба-зовых классов Objective-C и подходящий метод декодирования из таблицы 19.1 для базовых типов данных С

Таблица 19.1. Кодирование и декодирование базовых типов данных в архивах с ключами Метод кодирования Метод декодирования encodeBool:forKey: decodeBool:forKey: encodelnt:forKey: decodelnt:forKey: encodelnt32:forKey: decodelnt32:forKey: encodelnt64:forKey: decodelnt64:forKey: encodeFloat:forKey: decodeFloal:forKey: encodeDouble:forKey: decodeDouble:forKey:

В программе 19.5 в классы AddressCard и AddressBook добавлены методы кодирования и декодирования. #import <Foundation/NSObject.h> #import <Foundation/NSString.h> #import <Foundation/N$KeyedArchiver.h> @interface AddressCard: NSObject <NSCoding, NSCopying> { NSString *name; NSString *email; ) @property (copy, nonatomic) NSString *name, *email; -(void) setName: (NSString *) theName andEmail: (NSString *) theEmail; -(NSComparisonResult) compareNames: (id) element; -(void) print; // Дополнительные методы для протокола NSCopying -(AddressCard *) copyWithZone: (NSZone *) zone; -(void) retainName: (NSString *) theName andEmail: (NSString *) theEmail; @end

Следующие два метода, которые используются для класса AddressCard, должны быть добавлены в файл секции implementation. -(void) encodeWithCoder: (NSCoder *) encoder { [encoder encodeObject: name forKey: @"AddressCardName"]; [encoder encodeObject: email forKey: @"AddressCardEmair[; -(id) initWithCoder: (NSCoder *} decoder { name = [[decoder decodeObjectforKey: @"AddressCardName"] retain]; email = [[decoder decodeObjectforKey: @'AddressCardEmail"] retain]; return self; }

Метод кодирования encodeWithCoder: передается объекту NSCoder в качестве его аргумента.

Поскольку класс AddressCard наследует непосредственно из NSObject, нам не нужно заботиться о кодировании наследуемых переменных экземпляра. Если суперкласс вашего класса согласуется с протоколом NSCoding, то, чтобы обеспечить кодирование своих наследуемых переменных экземпляра, вы должны начать метод кодирования со строки [super encodeWithCoder: encoder];

Наша адресная книга содержит две переменные экземпляра с именами name н email. Поскольку это объекты класса NSString, мы можем использовать метод encodeObject:forKey: для кодирования каждой из них по порядку. Затем эти переменные экземпляра добавляются в архив.

Метод encodeObject:forKcy: кодирует объект и сохраняет его с указанным ключом для последующего считывания с помощью этого ключа. Имена ключей можно задавать произвольно, для считывания (декодирования) данных нужно использовать тот же ключ, который использовался для их архивации (коди-рования). Конфликт может возникнуть только в том случае, если тот же ключ используется для подкласса кодируемого объекта. Чтобы не возникла эта ситуация, можно вставить имя класса перед именем переменной экземпляра, когда вы составляете ключ для архива, как это сделано в программе 19.5.

Отметим, что encodeObject:forKey: можно использовать для любого объекта, в классе которого имеется соответствующий реализованный метод encodeWithCoder:.

Процесс декодирования действует в обратном порядке. Аргумент, переда-ваемый initWithCoder:, тоже являегся объектом NSCoder. Вам не нужно заботиться об этом ар|ументе; он получает сообщения для каждого объекта, который вы хотите извлечь из архива.

Поскольку в данном случае класс AddressCard наследует непосредственно из NSObject, вам не нужно заботиться о декодировании наследуемых переменных экземпляра. Достаточно вставить следующую строку в начало ваше метода декодирования (decoder), если ваш класс согласуется с протоколом NSCoding. self = [super initWithCoder: decoder];

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

Аналогично классу AddressCard, мы добавляем методы кодирования и декодирования в класс AddressBook. В файле секции interface нужно только изменить строку с директивой @interface, чтобы объявить, что теперь с протоколом NSCoding согласуется класс AddressBook. Это изменение может выглядеть следующим образом. @interface AddressBook: NSObject <NSCoding, NSCopying>

Ниже определяются методы для включения в файл секции implementation. -(void) encodeWithCoder: (NSCoder *) encoder { (encoder encodeObject: bookName forKey: "AddressBookBookName"]; (encoder encodeObject: book forKey: @"AddressBookBook"]; } -(id) initWithCoder: (NSCoder *} decoder { bookName = [[decoder decodeObjectForKey: @"AddressBookBookName"] retain]; book = [[decoder decodeObjectForKey: @"AddressBookBook"] retain]; return self; }

Программа 19.6 — это тестовая программа. #import «AddressBook. h» #import <Foundation/NSAutoreleasePool.h> int main (int argc, char *argv[]) { NSString *aName = @"Julia Kochan"; NSString *aEmail = @"jewls337@axlc.com"; NSString *bName = @"Tony lannino"; NSString *bEmail = @"tony.iannino@techfitness.com"; NSString *cName = @"Stephen Kochan"; NSString *cEmail = @"steve@steve_kochan.com''; NSString *dName = @"Jamie Baker"; NSString *dErnail = @"jbaker@hitmail.com"; NSAutoreteasePool * pool = [[NSAutoreleasePool alloc] init]; AddressCard *card1 = [[AddressCard alloc] ini:]; AddressCard *card2 = [[AddressCard alloc] init]; AddressCard *card3 = [[AddressCard alloc] initj; AddressCard *card4 = [[AddressCard alloc] init); AddressBook *myBook = [AddressBook alloc]; // Сначала задаем четыре адресные карточки [card 1 setName: aName andEmail: aEmail]; [card2 setName: bName andEmail: bEmail]; [card3 setName: cName andEmail: cEmail]; [card4 setName: dName andEmail: dEmail]; myBook = [myBook initWithName: (@"Steve's Address Book"]; // Добавляем несколько карточек в адресную книгу [myBook addCard: card 1 ]; [myBook addCard: card2]; [myBook addCard: card3]; [myBook addCard: card4]; [myBook sort]; if ([NSKeyedArchiver archiveRootObject: myBook toFile: @"addrbook.arch"] == NO) NSLog ((@"archiving failed"); [card 1 release]; [card2 release]; [card3 release]; [card4 release]; [myBook release]; [pool drain]; return 0; }

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