[精通Objective-C]归档和序列化

[精通Objective-C]归档和序列化

参考书籍:《精通Objective-C》【美】 Keith Lee

使用Foundation框架的归档和序列化类可以将对象(对象图)转换为具有独立结构的字节缓冲区。这样就可以将数据写入文件或者传送给其他进程(通常会通过网络)。之后,这些数据可能会被转换回对象并保留相关的对象图。这些类为数据持久化提供了轻量级技巧。序列化处理类能够保存数据和对象在其层次结构中的位置,而归档处理类具有更广泛的用途,它们可以保存数据、数据类型和对象层次结构中对象之间的关系。

目录

  • 精通Objective-C归档和序列化

    • 目录
    • 归档
      • 序列化归档
      • 带键值的归档
      • 编码和解码对象
    • 属性列表序列化
    • 归档对象图示例
      • 创建对象图
      • 创建用于归档对象图的类
      • 测试结果

归档

NSCoder是一个抽象类,声明了可用于对对象图进行序列化和反序列化的接口。序列化处理可以将对象的信息转换为字节序列,而反序列化处理可以通过字节序列(通过先前的序列化操作获得的)创建对象。

序列化归档

使用NSArchiver类和NSUnarchiver类可以创建序列化归档,这意味着序列化归档中的对象和值必须使用编码时使用的顺序才能解码。此外,在解码序列化归档时,必须解码整个对象图。使用NSArchiver类可以编码对象,以便将其写入文件或做其他用途,而使用NSUnarchiver类可以通过解码归档文件获得对象。

带键值的归档

NSKeyedArchiver类和NSKeyedUnarchiver类是带键值的归档类,即归档中的每个值都可以具有独有的名称和键值。这些键值必须在被编码和解码的对象内具有唯一性。

下面是带键值归档的使用:

        // 使用NSKeyedArchiver类中的archiveRootObject:方法,将名为greeting的NSString实例归档到当前目录的greeting.archive文件中。
        NSString *greeting = @"Hello,World!";
        NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
        NSString *archivePath = [cwd stringByAppendingString:@"/greeting.archive"];
        BOOL result = [NSKeyedArchiver archiveRootObject:greeting toFile:archivePath];

        if (result) {
            // 使用NSKeyedUnarchiver类中的unarchiveObjectWithFile:方法对greeting.archive文件进行解码
            NSString *greeting2 = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
            NSLog(@"%@",greeting2);
        }

编码和解码对象

当使用NSKeyedArchiver类和NSKeyedUnarchiver类执行归档过程时,必须使用遵守NSCoding协议的类。该协议声明了以下两个方法(具体使用会在后面给出):

-(id)initWithCoder:(NSCoder *)aDecoder;

-(void)encodeWithCoder:(NSCoder *)aCoder;

属性列表序列化

使用NSPropertyListSerialization类可以通过编程方式创建属性列表。该类支持以下数据结构:

NSData
NSDate
NSNumber
NSString
NSArray
NSDictionary

下面是一个属性列表序列化的例子,

其中format参数:

NSPropertyListOpenStepFormat // 传统的ASCII码属性列表格式
NSPropertyListXMLFormat_v1_0 // XML属性列表格式
NSPropertyListBinaryFormat_v1_0 // 二进制属性列表格式

options参数:

NSPropertyListImmutable  // 返回的属性列表中含有不可变对象
NSPropertyListMutableContainers  // 返回的属性列表中含有可变容器,但其中的元素不可变
NSPropertyListMutableContainersAndLeaves  // 返回的属性列表中含有可变的容器和元素
        // 序列化属性列表
        NSError *errorStr;
        NSDictionary *data = @{@"FirstName":@"John",@"LastName":@"Doe"};
        NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:data format:NSPropertyListXMLFormat_v1_0 options:0 error:&errorStr];

        // 对属性列表进行反序列化
        NSError *errorStr2;
        NSDictionary *plist= [NSPropertyListSerialization propertyListWithData:plistData options:NSPropertyListImmutable format:NULL error:&errorStr2];
        NSLog(@"%@",plist);

归档对象图示例

下面使用Foundation框架归档API编写一个程序。该程序会通过类层次结构创建一个对象图,然后通过归档操作编码和解码该对象图。

创建对象图

首先创建一个继承于NSObject,遵循NSCoding协议的类Order:

#import <Foundation/Foundation.h>

@interface Order : NSObject <NSCoding>

@property(readonly) NSString *order;

// 自定义初始化方法
-(id)initWithOrder:(NSString *)order;

@end
#import "Order.h"

@implementation Order

-(id)initWithOrder:(NSString *)order{
    if ((self = [super init])) {
        _order = order;
    }
    return self;
}

// 实现NSCoding协议中的归档初始化方法
-(id)initWithCoder:(NSCoder *)aDecoder{
    if ((self = [super init])) {
        _order = [aDecoder decodeObjectForKey:@"ORDER_KEY"];
    }
    return self;
}

// 实现NSCoding协议中的编码方法
-(void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.order forKey:@"ORDER_KEY"];
}

-(NSString *)description{
    return [NSString stringWithFormat:@"Order:%@", self.order];
}

@end

类似地,接下来再创建一个继承于Order类的Family类和继承于Family类的SubFamily类:

Family类:

#import "Order.h"

@interface Family : Order

@property(readonly)NSString *family;

-(id)initWithFamily:(NSString *)family order:(NSString *)order;

@end
#import "Family.h"

@implementation Family

-(id)initWithFamily:(NSString *)family order:(NSString *)order{
    if ((self = [super initWithOrder:order])) {
        _family = family;
    }
    return self;
}

// 注意:这里调用的是父类的initWithCoder:方法,而不是init方法
-(id)initWithCoder:(NSCoder *)aDecoder{
    if ((self = [super initWithCoder:aDecoder])) {
        _family = [aDecoder decodeObjectForKey:@"FAMILY_KEY"];
    }
    return self;
}

// 注意:这里调用了父类的编码方法
-(void)encodeWithCoder:(NSCoder *)aCoder{
    [super encodeWithCoder:aCoder];
    [aCoder encodeObject:self.family forKey:@"FAMILY_KEY"];
}

-(NSString *)description{
    return [NSString stringWithFormat:@"Family:%@,%@",self.family,[super description]];
}

@end

SubFamily类:

#import "Family.h"

@interface SubFamily : Family

@property(readonly) NSString *genus;
@property(readonly) NSString *species;

-(id)initWithSpecies:(NSString *)species genus:(NSString *)genus family:(NSString *)family order:(NSString *)order;

@end
#import "SubFamily.h"

@implementation SubFamily

-(id)initWithSpecies:(NSString *)species genus:(NSString *)genus family:(NSString *)family order:(NSString *)order{
    if ((self = [super initWithFamily:family order:order])) {
        _species = species;
        _genus = genus;
    }
    return self;
}

-(id)initWithCoder:(NSCoder *)aDecoder{
    if ((self = [super initWithCoder:aDecoder])) {
        _species = [aDecoder decodeObjectForKey:@"SPECIES_KEY"];
        _genus = [aDecoder decodeObjectForKey:@"GENUS_KEY"];
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder{
    [super encodeWithCoder:aCoder];
    [aCoder encodeObject:self.species forKey:@"SPECIES_KEY"];
    [aCoder encodeObject:self.genus forKey:@"GENUS_KEY"];
}

-(NSString *)description{
    return [NSString stringWithFormat:@"Animal - Species:%@ Genus:%@,%@",self.species,self.genus,[super description]];
}

@end

创建用于归档对象图的类

接下来创建Archiver类:

#import <Foundation/Foundation.h>

@interface Archiver : NSObject

@property(readwrite) NSString *path;

-(BOOL) encodeArchiver:(id)data toFile:(NSString *)file;
-(id) decodeArchiverFromFile:(NSString *) file;

@end
@implementation Archiver

-(id) init{
    if ((self = [super init])) {
        _path = NSTemporaryDirectory();
    }
    return self;
}

-(BOOL) encodeArchiver:(id)data toFile:(NSString *)file{
    NSString *archivePath = [self.path stringByAppendingPathComponent:file];

    // 为了编码数据,创建一个归档对象
    NSMutableData *mdata = [[NSMutableData alloc] init];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mdata];

    // 编码将简单字符串作为键的数据
    [archiver encodeObject:data forKey:@"FELINE_KEY"];
    [archiver finishEncoding];

    // 将已编码的数据写入文件,返回写入操作的状态
    BOOL result = [mdata writeToFile:archivePath atomically:YES];
    return result;
}

-(id) decodeArchiverFromFile:(NSString *)file{
    // 获取归档文件的路径
    NSString *archivePath = [self.path stringByAppendingPathComponent:file];

    // 为了解码数据,创建解码对象
    NSData *data = [[NSMutableData alloc] initWithContentsOfFile:archivePath];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    // 解码将简单字符串作为键的数据
    id result = [unarchiver decodeObjectForKey:@"FELINE_KEY"];
    [unarchiver finishDecoding];

    return result;
}

@end

测试结果

最后编写main.m文件:

#import <Foundation/Foundation.h>
#import "Archiver.h"
#import "SubFamily.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 为了编码/解码对象图,创建归档对象
        Archiver *archiver = [[Archiver alloc] init];

        // 创建一个对象图并将之归档到文件中
        id animal = [[SubFamily alloc] initWithSpecies:@"Lion" genus:@"Panther" family:@"Felid" order:@"Carnivore"];
        NSLog(@"\n%@",[animal description]);
        NSString *file = @"data.archive";

        if ([archiver encodeArchiver:animal toFile:file]) {
            NSLog(@"You encoded an archive to file %@",[[archiver path] stringByAppendingString:file]);
        }

        // 通过对象图的归档和描述记录,解码对象图
        id data = [archiver decodeArchiverFromFile:file];
        if ([archiver decodeArchiverFromFile:file]) {
            NSLog(@"You decoded an archive from file %@\n%@",[[archiver path] stringByAppendingString:file],[data description]);
        }
    }
    return 0;
}

运行结果:

2016-07-13 13:51:28.799 Archiver[60039:1335323]
Animal - Species:Lion Genus:Panther,Family:Felid,Order:Carnivore
2016-07-13 13:51:28.800 Archiver[60039:1335323] You encoded an archive to file /var/folders/jz/y4kx8_2n1wj188xflspjrpb80000gn/T/data.archive
2016-07-13 13:51:28.801 Archiver[60039:1335323] You decoded an archive from file /var/folders/jz/y4kx8_2n1wj188xflspjrpb80000gn/T/data.archive
Animal - Species:Lion Genus:Panther,Family:Felid,Order:Carnivore
时间: 2024-12-21 01:53:37

[精通Objective-C]归档和序列化的相关文章

iOS 各种系统文件目录 临时,缓存,document,lib,归档,序列化

Java代码   /** 1:Documents:应用中用户数据可以放在这里,iTunes备份和恢复的时候会包括此目录 2:tmp:存放临时文件,iTunes不会备份和恢复此目录,此目录下文件可能会在应用退出后删除 3:Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除 */ NSArray *paths1=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomai

IOS文件操作和自定义对象的归档(序列化)、反归档(反序列化)

IOS对文件操作包含文件的创建,读写,移动,删除等操作. 1.文件的创建: //设定文本框存储文件的位置 NSString *strFilePath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]; //指定存储文件的文件名 NSString *fileName=[strFilePath stringByAppendingPathComponent:@

iOS文档序列化(对象归档)

对象归档: 概念: 对象归档是指将对象写入文件保存在硬盘,当再次重新打开程序时,可以还原这些对象.也称:对象序列化.对象持久化. 数据持久性的方式(其实就是3类) 1,NSKeyedArchiver--对象归档 2,NSUserDefaults  3,属性列表 4,SQLite数据库  5,Core Data数据库 归档方式: 对Foundation库中对象进行归档 自定义对象进行归档(需要实现归档协议,NSCoding) 归档与属性列表的区别: 1,归档后的文件是加密的,属性列表是明文的. 2

数据存储之归档解档

归档也叫序列化,是将文件存在硬盘,解码是从硬盘还原一.使用属性列表进行归档 如果对象是NSString,NSDictionary,NSArray,NSData,NSNumber,NSDate,可以是使用writeToFile:atomically方法将数据写到文件,注意这种方式是明文. NSArray *inputArray = [NSArray arrayWithObjects:@"abc", @"123", @"qiaohaibin"]; /

IOS数据持久化之归档NSKeyedArchiver

IOS数据持久化的方式分为三种: 属性列表 (自定义的Property List .NSUserDefaults) 归档 (NSKeyedArchiver) 数据库 (SQLite.Core Data.第三方类库等) 下面主要来介绍一个归档NSKeyedArchiver. 归档(又名序列化),把对象转为字节码,以文件的形式存储到磁盘上:程序运行过程中或者当再次重写打开程序的时候,可以通过解归档(反序列化)还原这些对象. 归档方式: 对Foundation框架中对象进行归档 对自定义的内容进行归档

IOS开发-数据持久化(一)【文本文件+二进制归档】

概要 数据持久化分为不同的方式,本章主要简示了数据归档(一般而说的序列化)和写XML的文本文件方式.其中XML文本方式主要使用NSArray或者NSDictionary的writeToFile方法,而数据归档使用了NSKeyedArchiver/NSKeyedUnarchiver等实现数据的归档(序列化). 结果展示 程序展示 数据化文件 注意新版本的IOS模拟器的目录和以前的目录不在同一个地方,其中plist文件是XML文件,可打开直接查看,而archi是归档的二进制文件. 流程概要 1.新建

iOS:文件归档和解归档的详解和使用

文件归档和解归档: 用途: 所谓文件归档,就是把需要存储的对象数据存储到沙盒的Documents目录下的文件中,即存储到了磁盘上,实现数据的持久性存储和备份.解归档,就是从磁盘上读取该文件下的数据,用来完成用户的需求.对象归档是将对象归档以文件的形式保存到磁盘中(也称为序列化,持久化),使用的时候读取该文件的保存路径的读取文件的内容(也称为接档,反序列化),(对象归档的文件是保密的,在磁盘上无法查看文件中的内容,而属性列表是明文的,可以查看). 区别: 通过文件归档产生的文件是不可见的,如果打开

归档(转)

如果是自定义类型的归档和解档,是按以下步骤实现 如果不是自定义类型,直接第四步就可以. 一.创建一个类User二.在User.h中遵循NSCoding协议 #import <Foundation/Foundation.h> @interface User : NSObject<NSCoding> { int _userAge;//例子 NSString *_userName;// } @property(nonatomic,assign)int userAge; @property

Swift中KVO(监听)的使用方法及注意事项

---恢复内容开始--- 相信研究swift语言的开发者都多多少少了解或者精通Objective—C语言,熟练掌握Objective—C语言的开发者,在学习swift语言的过程中,是比较快速,而又轻松的.本人就是一位熟练掌握OC语言,后开始研究的swift.在学习swift语言的过程中,笔者建议有OC基础的开发者,在写swift的代码过程中,再写一下OC中的代码,二者相互比较,相信你能找到快速学会swift语言的方法.资深,有耐心和有天赋的开发者,相信能在一周左右,能够运用swift开发项目.其