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 
#import "XWZDog.h"

extern NSString * const kName ;
extern NSString * const kAge ;
extern NSString * const kDog ;
@interface XWZPerson : NSObject
@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 

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

@interface XWZDog : NSObject
@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 

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

@interface XWZTool : NSObject
@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 *)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);
}
打印结果


(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 *)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);
}

打印结果:







两个人是一样的,三条狗有两条是一样的,第三条为什么不一样。这块真的是没有办法研究了,第三条狗是通过类方法解档,而且我们也不知道,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 *)touches withEvent:(UIEvent *)event {
    NSKeyedUnarchiver *keyedDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.codeData];

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


编码器类方法直接归档
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 *)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 = 
person1 = 
Printing description of person->_dog:

Printing description of person1->_dog:

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;

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

iOS数据存储之NSCoder_第1张图片
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 *)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:

Printing description of person->_name:
Jack
Printing description of person->_age:
(NSInteger) _age = 28

}

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

你可能感兴趣的:(iOS数据存储之NSCoder)