归档自定义对象

转: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;

程序运行结果以下所示:

相关文章
相关标签/搜索