iOS归档解档

归档与解档是iOS中序列化与反序列化的方式,需要实现 encodeWithCoder 和 initWithCoder 方法,实现方式有两种:第一种是分别为属性赋值;第二种是通过runtime机制,循环为属性赋值。

归档和解档:即将数据写入文件和从文件中读取数据。

此处以plist文件为例说明,

一、plist文件使用时的注意事项:

1.plist文件中仅支持写入Array,Dictionary,Boolean,Data,Date,Number,String类型。

2.如果想要将自定义类的对象数据写入plist文件,则需要将对象修改为NSData类型写入。

二、归档步骤:

1.自定义的类必须遵守NSCoding协议,重写其归档和解档的方法。

  • (void)encodeWithCoder:(NSCoder *)aCoder;
  • (nullable instancetype)initWithCoder:(NSCoder *)aDecoder;

2.使用[NSKeyedArchiver archivedDataWithRootObject:p1]方法将p1对象转化为NSData类型。

3.通过NSArray调用arrayWithObjects方法写入plist文件中。

三、解档步骤:

1.从plist文件中读取内容

2.调用 [NSKeyedUnarchiver unarchiveObjectWithData:arr[i]]方法将NSData对象转化为Person对象。


新建Teacher类

@interface Teacher : NSObject
 
@property (nonatomic, strong) NSString *grade;//年级
@property (nonatomic, assign) NSInteger studentCount;//学生人数
 
@end

调用序列化与反序列化方法

- (void)save {
    Teacher *t = [[Teacher alloc] init];
    t.grade = @"1年3班";
    t.studentCount = 40;
 
    NSString *plistFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"t.data"];
    [NSKeyedArchiver archiveRootObject:t toFile:plistFilePath];
}
 
- (void)get {
    NSString *plistFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"t.data"];
    Teacher *t = [NSKeyedUnarchiver unarchiveObjectWithFile:plistFilePath];
    NSLog(@"%@,%ld",t.grade, t.studentCount);
}

下面实现一下 encodeWithCoder 和 initWithCoder 方法。

第一种方式:

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.grade forKey:@"grade"];
    [coder encodeInteger:self.studentCount forKey:@"studentCount"];
}
 
- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        _grade = [coder decodeObjectForKey:@"grade"];
        _studentCount = [coder decodeIntegerForKey:@"studentCount"];
    }
    return self;
}

当属性很多或者新增属性时,这两个方法就显得庞大而且不利于维护,同时还要注意属性的类型,类型不同调用的方法也不同。如:encodeObject 和 encodeInteger。

第二种方式:需要导入 #import

- (void)encodeWithCoder:(NSCoder *)coder {
    unsigned int numberOfIvars = 0;
    //成员变量
    Ivar *ivars = class_copyIvarList([Teacher class], &numberOfIvars);
    
    for (const Ivar *p = ivars; p < ivars + numberOfIvars; p++) {
        Ivar const ivar = *p;
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        [coder encodeObject:[self valueForKey:key] forKey:key];
    }
    free(ivars);
}
 
- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        unsigned int numberOfIvars = 0;
        //成员变量
        Ivar *ivars = class_copyIvarList([Teacher class], &numberOfIvars);
        
        for (const Ivar *p = ivars; p < ivars + numberOfIvars; p++) {
            Ivar const ivar = *p;
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            [self setValue:[coder decodeObjectForKey:key] forKey:key];
        }
        free(ivars);
    }
    return self;
}

这种方式通过runtime机制,循环遍历属性进行操作,非常方便,不用维护。网上好多文章也是这么写的,貌似没有问题。

注意:当有以下这种继承关系的时候就有问题了。创建Person类,Teacher继承Person。

@interface Person : NSObject
 
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
 
@end
 
@interface Teacher : Person
 
@property (nonatomic, strong) NSString *grade;
@property (nonatomic, assign) NSInteger studentCount;
 
@end

此时序列化并不会保存父类的属性,只需对上面的代码稍作修改就可以了。

- (void)encodeWithCoder:(NSCoder *)coder {
    Class cls = [self class];
    while (cls != [NSObject class]) {
        unsigned int numberOfIvars = 0;
        //成员变量
        Ivar *ivars = class_copyIvarList([cls class], &numberOfIvars);
        
        for (const Ivar *p = ivars; p < ivars + numberOfIvars; p++) {
            Ivar const ivar = *p;
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            [coder encodeObject:[self valueForKey:key] forKey:key];
        }
        free(ivars);
        
        cls = class_getSuperclass(cls);
    }
}
 
- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        Class cls = [self class];
        while (cls != [NSObject class]) {
            unsigned int numberOfIvars = 0;
            //成员变量
            Ivar *ivars = class_copyIvarList([cls class], &numberOfIvars);
            
            for (const Ivar *p = ivars; p < ivars + numberOfIvars; p++) {
                Ivar const ivar = *p;
                NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
                [self setValue:[coder decodeObjectForKey:key] forKey:key];
            }
            free(ivars);
            
            cls = class_getSuperclass(cls);
        }
    }
    return self;
}

————————————————

原文链接:https://blog.csdn.net/junjie_zhang/article/details/100537797

你可能感兴趣的:(iOS归档解档)