天天看點

OC基礎17:歸檔

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]];