1、歸檔即是用某種格式把一個或多個對象儲存起來,以便以後還原回來的一個過程。一般歸檔資料有兩種方法:屬性清單歸檔和帶鍵值的歸檔。
2、使用XML屬性清單進行歸檔:
(1)、Mac OS X上的應用程式使用XML屬性清單(或plists)來存儲諸如預設參數選擇、應用程式設定和配置資訊這樣的一些資料。然而這些清單的歸檔用途是有限的,因為當某個資料結建構立屬性清單時,沒有儲存特定的對象類,沒有存儲對同一對象的多個引用,也沒有保持對象的可變性;
(2)、如果你的對象是NSString、NSDictionary、NSArray、NSData或NSNumber對象,你可以使用在這些類中實作的writeToFile:atomically:方法将資料寫到檔案中。該方法可以使用XML屬性清單的格式寫出資料。以下代碼示範了如何将字典作為屬性清單寫入檔案中,和如何讀取出來:
...
NSDictionary *glossary = [NSDictionary dictionaryWithObjectsAndKeys:
@"A class defined so other class can inherit from it.",@"abstract class",
@"To implement all the methods defined in a protocol.",@"adopt",
@"Storing an object for later use.",@"archiving",
nil
];
if([glossary writeToFile:@"glossary" atomically:YES] == NO)
NSLog(@"Save to file failed!");
...
NSDictionary *readgloss;
readgloss = [NSDictionary dictionaryWithContentsOfFile:@"glossary"];
for(NSString *key in readgloss) {
NSLog(@"%@: %@",key,[readgloss objectForKey:key]);
}
...
(3)、其中writeToFile:atomically:消息被發送給字典對象glossary,使字典以屬性清單的形式寫入到檔案glossary中。atomically參數被設定為YES,表示希望首先将字典寫入臨時備份檔案中,并且一旦成功,再把最終資料轉移到名為glossary的制定檔案中。這是一種安全措施,它保護檔案在一些情況下(如系統在執行操作的過程中崩潰時)免受破壞。在這種情況下原始的glossary檔案(如果該檔案已經存在)不會受到損害;
(4)、writeToFile:atomically:所建立的glossary檔案,内容一般是這樣的:
<dict>
<key>abstract class</key>
<string>A class defined so other class can inherit from it.</string>
<key>adopt</key>
<string>To implement all the methods defined in a protocol.</string>
<key>archiving</key>
<string>Storing an object for later use.</string>
</dict>
(5)、根據字典建立屬性清單時,字典中的鍵必須全都是NSString對象。數組的元素或字典中的值可以是NSString、NSArray、NSDictionary、NSData或NSNumber等其他對象;
(6)、如果要将檔案中的XML屬性清單讀入程式中,要使用dictionaryWithContentsOfFile:方法或arrayWithContentsOfFile:方法。要讀取資料則用dataWithContentsOfFile:方法,要讀取字元串對象則用stringWithContentsOfFile:方法。
3、使用NSKeyedArchiver歸檔:
(1)、要将各種類型的對象(不僅僅是字元串、數組和字典類型的對象)存儲到檔案中,有一種更靈活的方法,就是利用NSKeyedArchiver類建立帶鍵的檔案來完成;
(2)、在帶鍵的歸檔中,每個歸檔字段都有一個名稱。歸檔某個對象時,會為它提供一個名稱,即鍵。從歸檔中檢索該對象時,是根據這個鍵來檢索的。這樣可以按照任意的順序将對象寫入歸檔并進行檢索。另外,如果向類添加了新的執行個體變量或删除了執行個體變量,程式也可以進行處理;
(3)、另外,iPhone SDK中沒有提供NSArchiver。如果想在iPhone上使用歸檔功能,則必須使用NSKeyedArchiver;
(4)、以下代碼示範了如何歸檔和讀取:
...
NSDictionary *glossary = [NSDictionary dictionaryWithObjectsAndKeys:
@"A class defined so other class can inherit from it.",@"abstract class",
@"To implement all the methods defined in a protocol.",@"adopt",
@"Storing an object for later use.",@"archiving",
nil
];
[NSKeyedArchiver archiveRootObject:glossary toFile:@"glossary.archive"];
...
NSDictionary *readglossary;
readglossary = [NSKeyedUnarchiver unarchiveObjectWithFile:@"glossary.archive"];
for(NSString *key in readglossary) {
NSLog(@"%@: %@",key,[readglossary objectForKey:key]);
}
...
4、歸檔自定義類:
(1)、歸檔自定義類,首先要實作<NSCoding>協定,然後實作encodeWithCoder:方法和initWithCoder:方法;
(2)、以AddressCard類為例,首先要實作<NSCoding>協定:
@interface AddressCard: NSObject <NSCoding>
然後在@implementation部分添加以下方法:
-(void) encodeWithCoder: (NSCoder *) encoder {
[super encodeWithCoder: encoder];
[encoder encodeObject: name forKey: @ “AddressCardName”];
[encoder encodeObject: email forKey: @ “AddressCardEmail”];
}
-(id) initWithCoder: (NSCoder *) decoder {
self = [super initWithCoder: decoder];
name = [decoder decodeObjectForKey: @”AddressCardName”];
email = [decoder decodeObjectForKey: @”AddressCardEmail”];
return self;
}
(3)、由于name和email兩個變量都是NSString類對象,是以可以使用encodeWithObject:方法對它們進行編碼;
(4)、有可能會有子類繼承了執行個體變量并且也進行歸檔,那麼如果某個執行個體變量歸檔時的key隻使用變量名的,就有可能會出現沖突。是以在指定key的時候,在執行個體變量名前加上類名;
(5)、如果父類也有編碼和解碼方法,才需要使用super語句;
(6)、bool、int、float和double等基本資料類型有各自對應的編碼和解碼方法;
(7)、歸檔和解碼的方法如下:
...
if([NSKeyedArchiver archiveRootObject: ac toFile: @”addresscard.arch”] == NO){
//ac是一個AddressCard類的對象
NSLog(@”archiving failed”);
}
...
ac = [NSKeyedUnarchiver unarchiveObjectWithFile: @”addresscard.arch”];
...
(8)、如果一個類中含有多個類型的執行個體變量,則在重載encodeWithCoder和initWithCoder兩個方法的時候,要對應使用不同的編碼方法和解碼方法,假如一個類Foo,包含了3個不同類型的執行個體變量,那麼重載方法如下:
...
-(void)encodeWithCoder: (NSCoder *) encoder {
[encoder encodeObject: strVal forKey: @”FooStrVal”];
[encoder encodeInt: intVal forKey: @”FooIntVal”];
[encoder encodeFloat: floatVal forKey: @”FooFloatVal”];
}
...
-(id) initWithCoder: (NSCoder *) decoder {
strVal = [decoder decodeObjectForKey: @” FooStrVal”];
intVal = [decoder decodeIntForKey: @” FooIntVal”];
floatVal = [decoder decodeFloatForKey: @” FooFloatVal”];
return self;
}
...
5、使用NSData建立自定義檔案:
(1)、有時可能想收集多個對象,并且将它們存儲到單個檔案檔案中去,那麼就要使用到NSData類;
(2)、以前面的AddressCard類和Foo類為例,兩個類都已實作了encodeWithCoder:方法和initWithCoder:方法,那麼可以使用encodeObject: forKey:方法把它們作為對象來歸檔;
(3)、歸檔的實作代碼如下:
...
NSMutableData *dataArea;
NSKeyedArchiver *archiver;
...
dataArea = [NSMutableData data];
archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: dataArea];
[archiver encodeObject: myCard forKey: @”myaddcard”];
[archiver encodeObject: myFoo forKey: @”myfoo”];
[archiver finishEncoding];
if([dataArea writeToFile: @”myArchive” atomically: YES] == NO){
NSLog(@”Archiver failed”);
}
...
(4)、其實就是使用定義過的encodeWithCode:r方法把對象歸檔到一個NSKeyedArchiver類對象archiver裡,最後使用XML屬性清單歸檔的方法把archiver的内容歸檔到檔案中(archiver的内容會寫在dataArea中),在過程中要注意,要向archiver發送一條finishEncoding的消息來結束編碼過程;
(5)、從檔案中恢複資料的方法如下:
...
NSData *dataArea;
NSKeyedUnarchiver *unarchiver;
...
dataArea = [NSData dataWithContentOfFile: @”myArchive”];
if(! dataArea) {
NSLog(@”can’t read back archiver file”);
return 1;
}
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData: dataArea];
myCard = [unarchiver decodeObjectForKey: @”myaddcard”];
myFoo = [unarchiver decodeObjectForKey: @”myfoo”];
[unarchiver finishDecoding];
...
(6)、同樣需要注意,最後要發送一條finishDecoding消息給unarchiver對象結束恢複;
6、可以使用Foundation的歸檔功能來建立對象的深複制:
(1)、假設有數組array1并且數組内有資料,可以使用NSData類對象data将它指派到另一個空數組array2中去,語句如下:
...
data = [NSKeyedArchiver archivedDataWithRootObject: array1];
array2 = [NSKeyedUnarchiver unarchiveObjectWithData: data];
...
(2)、其實就是将array1的資料歸檔到data中,然後再恢複到array2中,執行的效果就是完全的深複制了;
(3)、甚至可以省略中間的data對象,隻用一條語句來執行:
array2 = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject: array1]];