zoukankan      html  css  js  c++  java
  • 归档自定义对象

    转:http://blog.csdn.net/kmyhy/article/details/8626478

    Cocoa中,归档数据到文件,使用NSKeyedArchiver的archiveRootObject:toFile:方法。对于一般的数据类型(例如字符串),这个步骤是非常简单的。Apple官方文档中,这些数据类型包括:

        NSData

        NSString

        NSNumber

        NSDate

        NSArray

        NSDictionary

    很显然,复杂数据例如UIImage,无法直接归档。但我们有一种变通的做法,先将UIImage对象转换为NSData,再对NSData进行归档。1

    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;

    程序运行结果如下所示:

  • 相关阅读:
    浏览器加载AMD标准的输出文件
    Mac安装brew && brew 安装yarn
    插件集
    vue-router复用组件时不刷新数据
    加入sass后运行项目报错:TypeError: this.getResolve is not a function
    安装cnpm后运行报cnpm : 无法加载文件 C:UsersyizonAppDataRoaming pmcnpm.ps1,因为在此系统上禁止运行脚本
    图片canvas跨域问题解决方案之一
    vscode配置
    搭建express服务
    项目初始化
  • 原文地址:https://www.cnblogs.com/ygm900/p/4663387.html
Copyright © 2011-2022 走看看