ios runtime专题iOS学习专题iOS开发攻城狮的集散地

Runtime实现iOS对象的归档和解档

2018-08-03  本文已影响19人  wuyukobe

对遵循了NSCoding协议的iOS对象进行归档和解档是我们经常用到的一种数据持久化方式。

@interface People : NSObject<NSCoding>
//需要归档的属性
@property(nonatomic,copy)NSString * time;
@property(nonatomic,copy)NSArray * dataArray;
@end
#pragma mark - 实现NSCoding的协议方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
    //需要归档的属性
    [aCoder encodeObject:self.time forKey:@"time"];
    [aCoder encodeObject:self.dataArray forKey:@"dataArray"];
    NSLog(@"调用了 encodeWithCoder:");
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        //解档
        self.time = [aDecoder decodeObjectForKey:@"time"];
        self.dataArray = [aDecoder decodeObjectForKey:@"dataArray"];
        NSLog(@"调用了 initWithCoder:");
    }
    return self;
}

这样虽然能够实现People的归档和解档,但如果People类有许多个属性,那么我们需要对每个属性都实现一遍encodeObject:forKey:decodeObjectForKey:方法,这样就会显得比较繁琐,但是我们可以利用Runtime获取所有属性来重写归档解档方法。如下:

.h中声明两个归档解档的方法:

@interface NSObject (Runtime)
- (void)encoder:(NSCoder *)aCoder;
- (void)decoder:(NSCoder *)aDecoder;
@end

.m中利用Runtime重写归档解档的方法:

- (void)encoder:(NSCoder *)aCoder {
    unsigned int count = 0;
    Ivar * ivars = class_copyIvarList([self class], &count);
    for (int i=0; i<count; i++) {
        Ivar ivar = ivars[i];
        NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        id value = [self valueForKey:key];
        [aCoder encodeObject:value forKey:key];
    }
    //在OC中使用了Copy、Creat、New类型的函数,需要释放指针!(注:ARC管不了C函数)
    free(ivars);
}

- (void)decoder:(NSCoder *)aDecoder {
    unsigned int count = 0;
    Ivar * ivars = class_copyIvarList([self class], &count);
    for (int i=0; i<count; i++) {
        Ivar ivar = ivars[i];
        NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        id value = [aDecoder decodeObjectForKey:key];
        [self setValue:value forKey:key];
    }
    free(ivars);
}

并在NSCoding的协议方法中分别调用这两个方法:

#pragma mark - 实现NSCoding的协议方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
    //需要归档的属性
    [self encoder:aCoder];
    NSLog(@"调用了 encodeWithCoder:");
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        //解档
        [self decoder:aDecoder];
        NSLog(@"调用了 initWithCoder:");
    }
    return self;
}

这样在分类中调用的好处是其他遵循了NSCoding协议的对象想要实现归档和解档只需要导入头文件#import "NSObject+Runtime.h”并在NSCoding的协议方法中调用[self encoder:aCoder];[self decoder:aDecoder];即可。

People * p = [[People alloc]init];
p.time = @"今天";
p.dataArray = @[@"encode",@"decode"].copy;
//归档
[NSKeyedArchiver archiveRootObject:p toFile:self.filePath];
//解档
People * people = [NSKeyedUnarchiver unarchiveObjectWithFile:self.filePath];
NSLog(@"解档:%@,%@",people.time,people.dataArray);

打印结果为:

2018-08-03 17:07:51.586114+0800 WXQNSCoding_Runtime[7699:318222] 调用了 encodeWithCoder:
2018-08-03 17:07:51.587473+0800 WXQNSCoding_Runtime[7699:318222] 调用了 initWithCoder:
2018-08-03 17:07:51.587695+0800 WXQNSCoding_Runtime[7699:318222] 解档:今天,(
    encode,
    decode
)
我们也可以把实现NSCoding的协议方法定义为一个宏,这样我们一个宏K_NSCODING_METHOD就搞定了NSCoding的协议方法的实现:
#define K_NSCODING_METHOD \
- (void)encodeWithCoder:(NSCoder *)aCoder { \
[self encoder:aCoder]; \
NSLog(@"调用了 encodeWithCoder:"); \
} \
\
- (instancetype)initWithCoder:(NSCoder *)aDecoder { \
    self = [super init]; \
    if (self) { \
        [self decoder:aDecoder]; \
        NSLog(@"调用了 initWithCoder:"); \
    } \
    return self; \
} \

Demo地址:Runtime实现iOS对象的归档和解档

上一篇下一篇

猜你喜欢

热点阅读