iOS数据存储之NSCoder

资源连接:

iOS数据库存储之SQL语句;

iOS数据库存储之SQLite3;

iOS数据存储之文件沙盒;

iOS的本地存储技术

  1. Plist文件(NSArray\NSDictionary)

    Property List,属性列表文件,它是一种用来存储串行化后的对象的文件。属性列表文件的扩展名为.plist ,因此通常被称为 plist文件。文件是xml格式的。
    Plist文件通常用于储存用户设置,也可以用于存储捆绑的信息

  2. Preference(偏好设置\NSUserDefaults)

    用户偏好设置,plist格式文档,文档存储在沙盒Library/Preference下,系统提供[NSUserDefaults standardUserDefaults]单例类直接操作文档

  3. NSCoder(NSKeyedArchiver\NSkeyedUnarchiver)

    本文章重点介绍

  4. SQLite3

    数据库,用于处理相对大量的数据(详情请参考顶部连接)。

  5. Core Data

    SQLite数据库的面向对象封装,体量比SQLite3大很多。

NSCoder

简介:NSCoder是个抽象类,该类提供了用于存储和读取对象和其他值的接口(在内存和其他数据形式之间),但是必须要使用它的子类。此功能提供了归档的基础(对象和数据项存储在磁盘上)和分配(对象和数据项在不同进程或线程之间复制)。这些目的的子类包括NSArchiver,NSUnarchiver,NSKeyedarchiver,NSKeyedunarchiver,和NSPortCoder。NSCoder的具体子类统称为编码器类和这些类的实例作为编码对象(或简单的编码)。只能编码值的编码器对象称为编码器对象,并且只能将值解码的对象称为为解码器对象。

Tip:

  1. NSPortCoder 和 NSCoder还有NSUnarchiver 在Xcode8.3没有找到对应的累,也就说iOS项目中,iOS10以及之前的版本中Foundation框架中并没有对应的类。这里我们只找到了NSKeyedarchiver和NSKeyedunarchiver
  1. 对象,标量,C++数组、结构、指针、字符串,以及这些类型。它不处理跨平台执行不同的类型,如联合体、void *、函数指针和指针的长链。编码器对象将对象类型信息与数据一起存储,因此从字节流中解码的对象通常与最初编码到流中的对象相同。然而,当编码对象时,我们可以改变它的类型。

  2. AV Foundation框架给NSCoder类添加了方法(应该是分类方法,但具体声明在哪个文件,这里不做查找),这些方法使创建包括核心媒体的时间结构的归档更加容易,并从中提取核心媒体档案的时间结构。

NSCoding

这是一个编码协议,遵守此协议,并且实现协议方法,就可以使用NSCoder进行编码解码了,Cocoa框架大部分类都遵守了此协议。NSArray遵守了NSSecureCoding,NSSecureCoding 遵守了NSCoding。因此NSArray对于列表数据编码解码提供了很好的支持。

// 用编码器编码对象的时候,会调用该对象的此方法
- (void)encodeWithCoder:(NSCoder *)aCoder;
// 用解码器解码的时候会调用该对象的此方法
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder;

Note:解码器解码的时候怎么判断是哪一个类呢?后面会有解释,请坚持看完。
一般情况下我们还需要提供NSCoping协议支持,协议方法如下(完全看个人需求)
-(id)copyWithZone:(NSZone *)zone {
}

基本实例

通过基本实例我们可以熟悉、总结

三个对象类
//
//  XWZPerson.h
//  NSCodingDemo
//
//  Created by XinWeizhou on 2017/5/12.
//  Copyright © 2017年 XinWeizhou. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "XWZDog.h"

extern NSString * const kName ;
extern NSString * const kAge ;
extern NSString * const kDog ;
@interface XWZPerson : NSObject<NSCoding>
@property(nonatomic,strong) NSString *name;
@property(nonatomic,assign) NSInteger age;

@property(nonatomic,strong) XWZDog *dog;

@end

#import "XWZPerson.h"
NSString * const kName = @"name";
NSString * const kAge = @"age";
NSString * const kDog = @"dog";
@implementation XWZPerson
//+ (BOOL)supportsSecureCoding {
//    return YES;
//}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    _name = [aDecoder decodeObjectForKey:kName];
    _age = [aDecoder decodeIntegerForKey:kAge];
    _dog = [aDecoder decodeObjectForKey:kDog];
    NSLog(@"%s",__func__);
    return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_name forKey:kName];
    [aCoder encodeInteger:_age forKey:kAge];
    [aCoder encodeObject:_dog forKey:kDog];
    NSLog(@"%s",__func__);
}
@end

//
//  XWZDog.h
//  NSCodingDemo
//
//  Created by XinWeizhou on 2017/5/12.
//  Copyright © 2017年 XinWeizhou. All rights reserved.
//

#import <Foundation/Foundation.h>

extern NSString * const kName ;
extern NSString * const kAge ;

@interface XWZDog : NSObject<NSCoding>
@property(nonatomic,strong) NSString *name;
@property(nonatomic,assign) NSInteger age;
@end

#import "XWZDog.h"

@implementation XWZDog
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    _name = [aDecoder decodeObjectForKey:kName];
    _age = [aDecoder decodeIntegerForKey:kAge];
    NSLog(@"%s",__func__);
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_name forKey:kName];
    [aCoder encodeInteger:_age forKey:kAge];
    NSLog(@"%s",__func__);
}
@end

//
//  XWZTool.h
//  NSCodingDemo
//
//  Created by XinWeizhou on 2017/5/12.
//  Copyright © 2017年 XinWeizhou. All rights reserved.
//

#import <Foundation/Foundation.h>

extern NSString * const kName ;
extern NSString * const kAge ;

@interface XWZTool : NSObject<NSCoding>
@property(nonatomic,strong) NSString *name;
@property(nonatomic,assign) NSInteger age;
@end

#import "XWZTool.h"

@implementation XWZTool
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    _name = [aDecoder decodeObjectForKey:kName];
    _age = [aDecoder decodeIntegerForKey:kAge];
    NSLog(@"%s",__func__);
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    NSLog(@"%s",__func__);
    [aCoder encodeObject:_name forKey:kName];
    [aCoder encodeInteger:_age forKey:kAge];
}
@end

多对象归档
    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *keyedDecoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    
    XWZTool *tool = [XWZTool new];
    tool.name = @"chuTou";
    tool.age = 2;
    [keyedDecoder encodeObject:tool forKey:@"tool"];
    
    XWZPerson *person = [XWZPerson new];
    person.name = @"Jack";
    person.age = 28;
    [keyedDecoder encodeObject:person forKey:@"person"];
    [keyedDecoder finishEncoding];
    
    XWZDog *dog = [XWZDog new];
    dog.name = @"mengmeng";
    dog.age = 3;
    NSData *dataRoot = [NSKeyedArchiver archivedDataWithRootObject:dog];

   ***先注释掉***
   //  [data appendData:dataRoot];
    self.codeData = data;

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSKeyedUnarchiver *keyedDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.codeData];
    
    XWZTool *tool = [keyedDecoder decodeObjectForKey:@"tool"];
    XWZPerson *person = [keyedDecoder decodeObjectForKey:@"person"];
   
    XWZDog *dog = [keyedDecoder decodeObjectForKey:NSKeyedArchiveRootObjectKey];
    XWZDog *dog1 = [NSKeyedUnarchiver unarchiveObjectWithData:self.codeData];
    
    [keyedDecoder finishDecoding];
   
    NSLog(@"\n%@\n%@\n%@\n%@",tool,person,dog,dog1);
}
打印结果
<XWZTool: 0x600000036000>
<XWZPerson: 0x600000036080>
(null)
(null)

// 当我们把上面的一行注释代码***先注释掉*** [data appendData:dataRoot];还原,打印结果如下:
(null)
(null)
(null)
(null)

看来编码之后的data数据不能随便改动,那怎么用它的NSKeyedArchiveRootObjectKey呢,把它当普通的key使用,继续往下研究,看代码

    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *keyedDecoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    
    XWZTool *tool = [XWZTool new];
    tool.name = @"chuTou";
    tool.age = 2;
    [keyedDecoder encodeObject:tool forKey:@"tool"];
    
    XWZPerson *person = [XWZPerson new];
    person.name = @"Jack";
    person.age = 28;
    [keyedDecoder encodeObject:person forKey:@"person"];
    
    XWZDog *dog = [XWZDog new];
    dog.name = @"mengmeng";
    dog.age = 3;
    [keyedDecoder encodeObject:dog forKey:NSKeyedArchiveRootObjectKey];
    
    [keyedDecoder finishEncoding];
    self.codeData = data;

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSKeyedUnarchiver *keyedDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.codeData];
    
    XWZTool *tool = [keyedDecoder decodeObjectForKey:@"tool"];
    
    XWZPerson *person = [keyedDecoder decodeObjectForKey:@"person"];
    XWZPerson *person1 = [keyedDecoder decodeObjectForKey:@"person"];
    
    XWZDog *dog = [keyedDecoder decodeObjectForKey:NSKeyedArchiveRootObjectKey];
    XWZDog *dog1 = [keyedDecoder decodeObjectForKey:NSKeyedArchiveRootObjectKey];
    
    XWZDog *dog2 = [NSKeyedUnarchiver unarchiveObjectWithData:self.codeData];
    
    [keyedDecoder finishDecoding];
    
    
    NSLog(@"\n%@\n%@\n%@\n%@\n%@\n%@",tool,person,person1,dog,dog1,dog2);
}

打印结果:
<XWZTool: 0x6000000316e0>
<XWZPerson: 0x608000032500>
<XWZPerson: 0x608000032500>
<XWZDog: 0x608000032740>
<XWZDog: 0x608000032740>
<XWZDog: 0x6080000327e0>

两个人是一样的,三条狗有两条是一样的,第三条为什么不一样。这块真的是没有办法研究了,第三条狗是通过类方法解档,而且我们也不知道,rootobject和其他object的具体层次关系。那妥协的结果就是我们会用就行了。下面再岩石一个组合类型:

对象组合归档
// 这是一个对象之间的组合
    NSMutableData *data = [[NSMutableData alloc] init];
    NSKeyedArchiver *keyedDecoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    
    XWZPerson *person = [XWZPerson new];
    person.name = @"Jack";
    person.age = 28;
    
    XWZDog *dog = [XWZDog new];
    dog.name = @"mengmeng";
    dog.age = 3;
    person.dog = dog;
    
    [keyedDecoder encodeObject:person forKey:@"person"];
    
    [keyedDecoder finishEncoding];
    self.codeData = data;

  - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSKeyedUnarchiver *keyedDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.codeData];

    XWZTool *person = [keyedDecoder decodeObjectForKey:@"person"];
    NSLog(@"person= %@",person);
  }
相应结果:
person= <XWZPerson: 0x600000037e80>
Printing description of person->_dog:
<XWZDog: 0x600000037f80>

编码器类方法直接归档
XWZPerson *person = [XWZPerson new];
    person.name = @"Jack";
    person.age = 28;
    
    XWZDog *dog = [XWZDog new];
    dog.name = @"mengmeng";
    dog.age = 3;
    person.dog = dog;
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person];
//    NSKeyedArchiver archiveRootObject:<#(nonnull id)#> toFile:<#(nonnull NSString *)#>
    
    self.codeData = data;

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    XWZPerson *person = [NSKeyedUnarchiver unarchiveObjectWithData:self.codeData];
//    [NSKeyedUnarchiver unarchiveObjectWithFile:<#(nonnull NSString *)#>];
    
    NSKeyedUnarchiver *keyedDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.codeData];
    XWZPerson *person1 = [keyedDecoder decodeObjectForKey:NSKeyedArchiveRootObjectKey];
    
    NSLog(@"\nperson0 = %@\nperson1 = %@",person,person1);
}
// 打印结果:
person0 = <XWZPerson: 0x600000031300>
person1 = <XWZPerson: 0x60800002f700>
Printing description of person->_dog:
<XWZDog: 0x600000031400>
Printing description of person1->_dog:
<XWZDog: 0x608000030aa0>

Note:其实数据要写入文件才算归档,前面没有加入这么一步,只是为了代码思路简介清晰。看上面的的注释掉的类方法,只是多了一步存储到本地的功能。文件名后缀一般用.archiver。

解码器寻找对应类

刚才也有抛砖,现在把玉抛出来。首先看一下几个方法:

NSKeyedArchiver
// 设置NSKeyedArchiver全局的对应类标签,这个标签会被加入到编码Data里
+ (void)setClassName:(nullable NSString *)codedName forClass:(Class)cls;
// 设置对应的一个编码器的对应类标签
- (void)setClassName:(nullable NSString *)codedName forClass:(Class)cls;
// 开始编码,一个对象(objv)数据对应一个索引值(key)
- (void)encodeObject:(nullable id)objv forKey:(NSString *)key;

// 为NSKeyedUnarchiver类标签设置全局的类型
NSKeyedUnarchiver
+ (void)setClass:(nullable Class)cls forClassName:(NSString *)codedName;
// 为一个编码器的对应标签设置对应类
- (void)setClass:(nullable Class)cls forClassName:(NSString *)codedName;
- (nullable id)decodeObjectForKey:(NSString *)key;

这些方法的用处总结如有错误请指出;在网上找了好半天也没有找到像样的解释,链接里要么各种广告,要么打不开。最后花了一个小时总结出来。又花了一个小时画了张图。

EncoderAndDecoder.png
[NSKeyedArchiver setClassName:@"hello" forClass:[XWZPerson class]];
XWZPerson *person = [XWZPerson new];
person.name = @"Jack";
person.age = 28;
[keyedDecoder encodeObject:person forKey:@"person"];

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

[NSKeyedUnarchiver setClass:[XWZDog class] forClassName:@"hello"];
NSKeyedUnarchiver *keyedDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.codeData];

 XWZPerson *person = [keyedDecoder decodeObjectForKey:@"person"];    
    
NSLog(@"person = %@",person);

// 人对象编码之后解码成了狗对象,打印结果: 
-[XWZDog initWithCoder:]
Printing description of person:
<XWZDog: 0x60800003aac0>
Printing description of person->_name:
Jack
Printing description of person->_age:
(NSInteger) _age = 28

}

Note:这里可以看出,NSKeyedUnarchiver的为类名标签设置类型,尤为重要,它可以改变解码出来的对象类型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容