iOS归档解档

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

新建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;
}

 

你可能感兴趣的:(iOS,runtime)