免费开源的iOS开发学习平台

归档:3-自定义对象归档/解档

当我们需要存储的数据类型是一些基本的数据类型时,例如:NSString、NSArray、NSDictionary,我们可以使用Plist文件来直接进行存储。但是当我们需要存储自定义对象时,使用Plist文件就变得不方便了,这时推荐使用归档来存储自定义对象。存储自定义对象在iOS开发中的使用是非常普遍的。

NSCoding协议

可以归档的对象在定义时都需要遵守NSCoding协议,并且实现协议中定义的归档、解档方法,如下所示。由于我们常用的基本数据类型,如NSString、NSArray、NSDictionary,苹果官方在定义时已经默认遵守了该协议并且实现了归档、解档方法,所以我们可以直接对基本数据类型进行归档解档操作,而自定义对象则需要手工实现。

  • NSCoding协议中的对象归档方法
- (void)encodeWithCoder:(NSCoder *)aCoder;
  • NSCoding协议中的对象解档方法
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder;

自定义对象归档/解档的主要步骤

当我们需要对自定义对象进行归档和解档时,通常需要按照如下步骤进行:

  • 新建自定义类,并遵守NSCoding协议;
  • 实现NSCoding协议中用于归档解档的方法;
  • 创建自定义对象,并使用NSKeyedArchiver以及NSKeyedUnarchiver类中的方法进行对象的归档与解档

示例代码

我们通过一个示例,介绍一下自定义对象归档与解档的方法。

  • 新建自定义Person类,并遵守NSCoding协议,在Person.h文件中,添加两个属性以及一个类方法
@interface Person : NSObject<NSCoding>

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSNumber *age;

+(instancetype)personWithDict:(NSDictionary *) dict;

@end
  • 在Person.m文件中,实现encodeWithCoder:以及initWithCoder:方法
//实现NSCoding协议中的归档方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeObject:self.age forKey:@"age"];
}
//实现NSCoding协议中的解档方法
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.age = [aDecoder decodeObjectForKey:@"age"];
    }
    return self;
}
  • 新建ArchiveService工具类,在ArchiveService.h文件中添加如下两个方法,用于对象的归档、解档操作
#import <Foundation/Foundation.h>
#import "Person.h"

@interface ArchiveService : NSObject

+ (BOOL) writeObjectToArchiveFile:(Person *) object;
+ (Person *) readObjectFromArchiveFile;

@end
  • 在ArchiveService.m文件中实现用于归档的方法
+ (BOOL) writeObjectToArchiveFile:(Person *) object {
    //获取文件路径
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *filePath = [docPath stringByAppendingPathComponent:@"person.archive"];
    
    //调用NSKeyedArchiver的类方法archiveRootObject:toFile:将对象归档(返回一个布尔值)
    return  [NSKeyedArchiver archiveRootObject:object toFile:filePath];
    
}
  • 在ArchiveService.m文件中实现用于解档的方法
+ (Person *) readObjectFromArchiveFile {
    //获取文件路径
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    NSString *filePath = [docPath stringByAppendingPathComponent:@"person.archive"];
    //调用NSKeyedUnarchiver的类方法unarchiveObjectWithFile:将文件解档
    Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    return person;
}
  • 使用Storyboard搭建一个界面,能够输入name以及age,并且添加两个按钮【写入归档文件】、【读取归档文件】,并与控制器类建立连线;

  • 实现【写入归档文件】按钮点击功能
- (IBAction)writeToArchive:(id)sender {
    NSDictionary *dict = @{@"name":self.nameTextField.text,
                           @"age":self.ageTextField.text};
    
    Person *person = [Person personWithDict:dict];
    
    if([ArchiveService writeObjectToArchiveFile:person]){
        NSLog(@"归档成功");
    };
}
  • 实现【读取归档文件】按钮点击功能
- (IBAction)readFromArchiive:(id)sender {
    Person *person = [ArchiveService readObjectFromArchiveFile];
    NSLog(@"person name: %@, person age:%@", person.name,person.age);
}
  • 运行后,输入name以及age的取值,点击【写入归档文件】,可以看到界面如下提示,并且在App的沙盒文件夹中新增person.archive文件

  • 点击【读取归档文件】按钮,可以从归档文件中读取存储的自定义对象,并打印出结果。

https://github.com/99ios/13.4.3