iOS 数据归档解档

简介

本篇文章使用NSKeyedArchiver和NSKeyedUnarchiver进行iOS的归档解档web

  • 归档:即将数据写入文件里。通常咱们app的数据都是在内存里,只要app关闭,数据就会丢失。可是将数据保存在文件里,就能将数据保存至本地,无论app关闭仍是重启设备,下次启动app的时候都可以读出来
  • 解档:就是将保存的数据从文件中读出来在程序中使用
  • 关于沙盒能够参考:iOS开发关于沙盒

普通数组的归档解档

归档

  • 首先获取文件归档路径
  • 使用方法 archivedDataWithRootObject: requiringSecureCoding: error: 将数据归档到path文件路径里面
  • 方法介绍:
    P1: 要归档的对象
    P2:一个布尔值,指示是否全部编码对象都必须符合NSSecureCoding
    P3:返回的错误
  • 使用方法 writeToFile: atomically: 查看是否归档成功
//沙盒根目录
    NSString *docPath = NSHomeDirectory();
    //完整的文件路径
    NSString *path = [docPath stringByAppendingPathComponent:@"Documents/numbers.plist"];
    NSArray *numbers = @[@"one",@"two"];
    
    //将数据归档到path文件路径里面
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:numbers requiringSecureCoding:NO error:nil];
    BOOL success = [data writeToFile:path atomically:NO];
    
    if (success) {
        NSLog(@"文件归档成功");
    } else {
        NSLog(@"文件归档失败");
    }

解档

  • 首先获取文件路径
  • 使用方法 dataWithContentsOfFile: 将文件数据化
  • 使用方法 unarchivedObjectOfClass: fromData: error: 转化数据,读取内容
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

    NSString *path = [docPath stringByAppendingPathComponent:@"numbers.plist"];
    NSData *data = [NSData dataWithContentsOfFile:path];
    NSArray *numbers = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:nil];
    NSLog(@"numbers = %@", numbers);

在这里插入图片描述

多个普通对象同时归档解档

上面的例子是将单个的数组或者单个字典归档到一个文件。其实咱们也能够将多个数组、字典、字符串、数组等内容归档到同一个文件里面去。数组

归档

  • 一样先建立归档对象,使用方法 initForWritingWithMutableData: 将归档的数据写入data
  • 使用方法 encodeObject: forKey: 以键值对形式存储归档数据
  • 结束归档
  • 将归档的数据写入文件
NSDictionary *dic = @{@"one":@"hello",
                          @"two":@1};
    NSInteger age = 10;
    
    NSMutableData *data = [NSMutableData data];
    
    //建立归档对象,归档的数据须要写入data
    NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    
    //键值对形式存储归档数据
    [arch encodeObject:dic forKey:@"Dic"];
    [arch encodeInteger:age forKey:@"Age"];
    
    //结束归档
    [arch finishEncoding];
    NSString *docPath = NSHomeDirectory();
    NSString *manyPath = [docPath stringByAppendingPathComponent:@"Documents/manyData.plist"];
    //将归档的数据写入文件
    [data writeToFile:manyPath atomically:YES];

解档

  • 先建立解档对象
  • 使用方法 initForReadingFromData: error: 将文件数据化
  • 根据键值获得对应数据
//建立解档对象
    NSString *docPath = NSHomeDirectory();
    NSString *manyPath = [docPath stringByAppendingPathComponent:@"Documents/manyData.plist"];
    NSData *arrayData = [NSData dataWithContentsOfFile:manyPath];
    NSKeyedUnarchiver *unarch = [[NSKeyedUnarchiver alloc] initForReadingFromData:arrayData error:nil];
    //根据键值获得对应数据
    NSDictionary *dic = [unarch decodeObjectForKey:@"Dic"];
    NSInteger age = [unarch decodeIntegerForKey:@"Age"];
    
    //解档结束
    [unarch finishDecoding];
    NSLog(@"%@", dic);
    NSLog(@"%ld", age);

在这里插入图片描述

自定义对象的归档解档

上面的例子,归档的内容都是系统Foundation框架提供的类以及一些基本的数据类型。
Foundation框架提供的类都是实现了NSCoding协议的,因此可以直接进行归档。
若是咱们本身自定义了一个类,例如Person类,是没法可以直接将这个类进行归档操做的。
若是想要对自定义的类建立出来的对象进行归档,咱们须要先实现NSCoding协议。app

Student.h

@interface Student : NSObject
<NSCoding>

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger grade;

@end

Student.m

#import "Student.h"

@implementation Student

//NSCoding协议方法:将须要归档的属性进行归档
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeInteger:self.grade forKey:@"grade"];
    [coder encodeObject:self.name forKey:@"name"];
}

//NSCoding协议方法:将须要解档的属性进行解档
- (instancetype)initWithCoder:(NSCoder *)coder {
    if (self = [super init]) {
        self.name = [coder decodeObjectForKey:@"name"];
        self.grade = [coder decodeIntegerForKey:@"grade"];
    }
    return self;
}

归档

此处使用另外一种获取目录方法框架

//沙盒ducument目录
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    //完整的文件路径
    NSString *path = [docPath stringByAppendingPathComponent:@"student.archiver"];
    
    Student *student = [[Student alloc] init];
    student.name = @"dd";
    student.grade = 22;
    
    //将数据归档到path文件路径里面
    BOOL success = [NSKeyedArchiver archiveRootObject:student toFile:path];
    
    if (success) {
        NSLog(@"归档成功");
    }else {
        NSLog(@"归档失败");
    }

解档

NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    //完整的文件路径
    NSString *path = [docPath stringByAppendingPathComponent:@"student.archiver"];
    
    Student *student = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    NSLog(@"student's name = %@, student's grade = %lu", student.name, student.grade);

在这里插入图片描述

归档失败

  • 第一种可能:文件夹不存在
    若是文件夹不存在,则建立一个
//文件夹是否存在
    BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:dir isDirectory:nil];
    if (!fileExists) {
        NSLog(@"文件夹不存在");
        NSError *error = nil;
        [[NSFileManager defaultManager] createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&error];
        if (error) {
            NSLog(@"error == %@", error.description);
        } else {
            NSLog(@"success");
        }
    }
  • 第二种可能:没有写入权限
    有些不熟悉的人可能会遇到这个问题:我在模拟器上归档操做是成功的,但是在真机上运行的时候却归档失败了。究竟是什么缘由?
    这个就是遇到了写权限的问题了。在模拟器上,由于是写在电脑上面的路径的,因此这个路径咱们有写权限。可是真机不同,真机上咱们通常状况下只能操做沙盒路径里面文件夹。没法在其余的地方写文件。
    沙盒根目录也没有写入权限,咱们通常在Documents目录写
//查看是否有写入权限
    if (![[NSFileManager defaultManager] isWritableFileAtPath:dir]) {
        NSLog(@"目录无写入权限");
    }

文章参考文献:【iOS】数据持久化:使用NSKeyedArchiver进行数据归档svg