歸檔自定義對象

轉:http://blog.csdn.net/kmyhy/article/details/8626478程序員

Cocoa中,歸檔數據到文件,使用NSKeyedArchiver的archiveRootObject:toFile:方法。對於通常的數據類型(例如字符串),這個步驟是很是簡單的。Apple官方文檔中,這些數據類型包括:編碼

    NSDataatom

    NSString.net

    NSNumber設計

    NSDatecode

    NSArrayorm

    NSDictionary對象

很顯然,複雜數據例如UIImage,沒法直接歸檔。但咱們有一種變通的作法,先將UIImage對象轉換爲NSData,再對NSData進行歸檔。1blog

1:準確地說,這依賴於iOS的版本。在iOS4中,UIImage未實現NSCoding協議,在iOS5中,UIImage實現了NSCoding協議。遞歸

對於自定義的類型,咱們也能夠參考這一作法,即先將將自定義類型轉換爲NSData,再對NSData進行歸檔。

問題在於,NSKeyedArchiver在歸檔一個自定義對象時,怎麼知道如何將一個自定義對象編碼爲一個NSData?並且,當咱們從文件中反歸檔時,NSKeyedUnarchiver怎麼知道將NSData轉變爲一個自定義對象?

這就是NSCoding 協議須要解決的問題。實際上,NSCoding協議規定的兩個方法,分別用於解決這兩個問題。

當NSKeyedArchiver 在歸檔一個對象時,將調用對象的encodeWithCoder:方法,用於將對象轉換爲NSData(或NSString等其餘5種類型);而NSKeyedUnarchiver在反歸檔一個對象時,則調用對象的initWithCoder:方法,用於將NSData(或NSString等其餘5種類型)轉換爲指定的對象類型。

上述6種類型(NSData、NSString等)在歸檔/反歸檔時顯得尤爲簡單,是由於蘋果已經爲咱們實現了NSCoding協議。

而自定義對象不一樣,須要程序員本身實現其NSCoding協議。

新建項目,建立一個類MyClass,在頭文件中聲明對NSCoding協議的實現。

而後爲他設計屬性以下:

@interface MyClass : NSObject<NSCoding>

@property(strong,nonatomic)NSString* name;

@property(assign,nonatomic)int age;

@property(assign,nonatomic)BOOL sex;

@end

而後,在實現中實現NSCoding協議:

@implementation MyClass

@synthesize name,age,sex;

-(void)encodeWithCoder:(NSCoder *)encoder{

    [encoderencodeObject:self.name forKey:@"name"];

    [encoderencodeObject:[NSNumber numberWithInt: self.age] forKey:@"age"];

    [encoderencodeObject:[NSNumber numberWithBool: self.sex] forKey:@"sex"];

}

- (id)initWithCoder:(NSCoder *)decoder {

    if (self = [superinit]) {

       self.name = [decoder decodeObjectForKey:@"name"];

       self.age =((NSNumber*)[decoderdecodeObjectForKey:@"age"]).intValue;

       self.sex =((NSNumber*) [decoderdecodeObjectForKey:@"sex"]).boolValue;

    }

    return self;  

}

@end

在encodeWithCoder:方法中,這兩句好像顯得有點多餘:

[encoder encodeObject:[NSNumber numberWithInt: self.age]forKey:@"age"];

[encoder encodeObject:[NSNumber numberWithBool: self.sex]forKey:@"sex"];

然而事實並非這樣的,encoder對象只能對已經實現了NSCoding的對象進行編碼(即轉換爲6種類型之一),對於int、BOOL這樣的簡單類型則不行。所以,咱們須要對age和sex屬性轉換爲其餘類型(6種類型之一),好比NSNumber。

一樣,在initWithCoder:方法中,咱們必須對decoder反編碼後的數據進行必要的轉換,將它們由NSNumber(由於編碼時咱們是用NSNumber存儲的)轉換爲相應屬性原來的類型,才能進行賦值:

self.age =((NSNumber*)[decoderdecodeObjectForKey:@"age"]).intValue;

self.sex =((NSNumber*) [decoder decodeObjectForKey:@"sex"]).boolValue;

接下來,咱們使用NSKeyedArchiver/NSKeyedUnarchiver來對MyClass進行歸檔/反歸檔。

打開ViewController.xib,設計以下UI界面:

 

 

使用Asistant Editor,建立必要的鏈接。這時ViewController.h文件內容將顯示以下:

#import "MyClass.h"

#define AppDocuments [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0]

@interface ViewController : UIViewController<UITextFieldDelegate>

@property (retain, nonatomic) IBOutlet UITextField *tfName;

@property (retain, nonatomic) IBOutlet UITextField *tfAge;

@property (retain, nonatomic) IBOutlet UISegmentedControl *segSex;

@property (retain, nonatomic) IBOutlet UIButton *button;

- (IBAction)archiveUnarchive:(id)sender;

@end

在ViewDidLoad方法中,加入語句

[self initUI:nil];

其中,initUI:方法定義以下:

-(void)initUI:(MyClass*)obj{

    if (obj==nil) {

       tfName.text=nil;

       tfAge.text=nil;

       segSex.selectedSegmentIndex=0;

}else{

       tfName.text=obj.name;

       tfAge.text=[NSString stringWithFormat:@"%d",obj.age];

       segSex.selectedSegmentIndex=obj.sex;

}

}

定義方法makeMyClassInstance,在這個方法中咱們經過用戶在界面是輸入的內容建立MyClass實例:

-(MyClass*)makeMyClassInstance{

    MyClass*obj=[[MyClass alloc]init];

   obj.name=tfName.text;

    obj.age=[tfAge.textintValue];

    obj.sex=[segSexselectedSegmentIndex]!=0;

    return [objautorelease];

}

定義按鈕事件的Action方法以下:

- (IBAction)archiveUnarchive:(id)sender {

   button.enabled=NO;

    NSString*filePath=[AppDocuments stringByAppendingPathComponent:@"customobject.txt"];

    if(button.tag==0) {

       if (tfName && tfName.text.length>0 && tfAge&& tfAge.text.length>0) {

           button.tag=1;

           [button setTitle:@"反歸檔" forState:UIControlStateNormal];

           MyClass* obj=[self makeMyClassInstance];

           [NSKeyedArchiver archiveRootObject:obj toFile:filePath];

           [self initUI:nil];

       }

       

    }else{

       button.tag=0;

       [button setTitle:@"歸檔" forState:UIControlStateNormal];

       MyClass* obj=[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

       [self initUI:obj];

    }

   button.enabled=YES;

}

運行程序,在界面中輸入一些信息,而後點擊「歸檔」按鈕。

歸檔後,用戶輸入將再次清空,「歸檔」按鈕將顯示爲「反歸檔」。在應用程序的documents目錄,咱們將找到歸檔文件customeobject.txt。

 

回到程序,點擊「反歸檔」按鈕,將再次從customobject.txt文件中獲取MyClass對象,並將對象屬性讀取到UI控件中。

除了直接將MyClass對象做爲RootObject(根對象)歸檔到文件之外,更一般的作法是將MyClass對象放入集合(Array或Dictionary),再對集合對象進行歸檔。

此外,MyClass能夠遞歸,即它的屬性能夠是另外一個MyClass對象而且這個屬性也能夠被歸檔和反歸檔。

咱們能夠在MyClass中增長屬性parent,而後在encodeWithCoder:方法中增長此句:

if(self.parent)[encoder encodeObject:self.parent forKey:@"parent"];

一樣在initWithCoder:方法中加入:

self.parent=[decoder decodeObjectForKey:@"parent"];

這樣,在makeMyClassInstance方法中,咱們能夠爲obj對象構造另外一個MyClass對象做爲他的parent屬性:

MyClass* parent=[[MyClass alloc]init];

parent.name=@"dad";

parent.age=obj.age+20;

parent.sex=0;

parent.parent=nil;

obj.parent=parent;

在反歸檔以後,咱們能夠在initUI方法中顯示parent的信息:

lbParent.text=obj.parent.name;

程序運行結果以下所示:

相關文章
相關標籤/搜索