使用Runtime对用户数据进行归档存储

需求:

  • 在 iOS 开发中,经常需要对用户的一些数据进行持久化的存储, 用以保证用户杀死 App 后, 在下次启动依然能使用退出前的一些数据。如: 用户登录后杀死 App ,下次进入不需要再次登录
  • 归档存储的两个问题:(1)存哪里?(2)怎么存?

使用技术:

ps:持久化的技术很多,这里只说归档
* 存哪里? 沙盒
- 沙盒结构如图
这里写图片描述

步骤

  1. 遵守NSCoding协议
  2. 实现归档、接档方法 (重点在这)
  3. 设置归档位置(存哪里)
  4. 开始归档、接档

第一版

//归档
- (void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.account forKey:@"accountKey"];
    [aCoder encodeObject:self.password forKey:@"passwordKey"];
}
//解档
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init]) {
        self.account = [aDecoder decodeObjectForKey:@"accountKey"];
        self.password = [aDecoder decodeObjectForKey:@"passwordKey"];
    }
    return self;
}
  • 如果这两个方法记不住,先遵守协议,然后command+点击 就能查看具体的协议定义内容,如下图这里写图片描述
  • 这种做法有缺陷: 归档和解档两个方法对同一属性的操作的key容易写错
    改一下出第二版

第二版

  • 改进一下,使用宏定义来代替key
#define ACCOUNT_KEY @"accountKey"
#define PASSWORD_KEY @"passwordKey"

定义了这两个宏,这样就可以在两个方法中对同一属性保持相同的key, 这样就不会取错了

//归档
- (void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.account forKey:ACCOUNT_KEY];
    [aCoder encodeObject:self.password forKey:PASSWORD_KEY];
}
//解档
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init]) {
        self.account = [aDecoder decodeObjectForKey:ACCOUNT_KEY];
        self.password = [aDecoder decodeObjectForKey:PASSWORD_KEY];
    }
    return self;
}
  • 这里的宏业可以用静态常量字符串代替
    这里写图片描述
  • 这样做的缺陷:如果有100个属性怎么办?那就需要100个宏,写100次归档和解档,这样工作量还是很大的。 所以下面我们引入运行时机制

第三版

//归档
- (void)encodeWithCoder:(NSCoder *)aCoder{
    unsigned int outCount = 0;
    Ivar *ivars = class_copyIvarList([self class], &outCount);
    for (int i = 0; i< outCount; i++) {
        Ivar ivar = ivars[i];
        const char *ivarName = ivar_getName(ivar);
        //获取到时是带有下划线的成员变量名
        NSString *ocStr = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding];
        //根据kvc的获取规则,即使是属性名和成员变量名不一致,也是可以获取到的,如:name 和 _name
        id value = [self valueForKey:ocStr];
        [aCoder encodeObject:value forKey:ocStr];
        NSLog(@"%@",ocStr);
    }
    free(ivars);
}
//解档
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([self class],&count);
        for (int i = 0; i < count; i++) {
            Ivar ivar = ivars[i];
            const char *ivarName = ivar_getName(ivar);
            NSString *ocStr = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding];
            id obj = [aDecoder decodeObjectForKey:ocStr];
            [self setValue:obj forKey:ocStr];
        }
        free(ivars);
    }
    return self;
}
  • KVC 中 setValue:forKey的搜索方式(name为例):
    1. setName: 2. _name 3._isName 4.name 5. isName 6. setValue:forUNdefinedKey:
  • KVC 中 valueForKey:的搜索方式(name为例):
    1. getName: 2. _name 3._isName 4.name 5. isName 6. valueForUndefinedKey:
  • 这种做法也有缺陷:如果有不同的类需要归档呢,就得写很多遍相同的代码。我们可以把它抽出来变成版本四

第四版

//
//  NSObject+LGArchive.m
//  RuntimeArchive
//
//  Created by lingo on 2018/2/28.
//  Copyright © 2018年 livefor. All rights reserved.
//

#import "NSObject+LGArchive.h"
#import <objc/runtime.h>
@implementation NSObject (LGArchive)
- (void)encode:(NSCoder *)aCoder{
    Class clazz = self.class;
    //如果有继承关系的情况,并且父类不是NSObject
    while (clazz && clazz != [NSObject class]) {
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(clazz, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            const char *ivarName = ivar_getName(ivar);
            //获取到时是带有下划线的成员变量名
            NSString *ocStr = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding];
            //根据kvc的获取规则,即使是属性名和成员变量名不一致,也是可以获取到的,如:name 和 _name
            id value = [self valueForKey:ocStr];
            [aCoder encodeObject:value forKey:ocStr];
        }
        free(ivars);
        clazz = [clazz superclass];
    }
}

- (void)decode:(NSCoder *)aDecoder {
    Class clazz = self.class;
    while (clazz && clazz != [NSObject class]) {
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(clazz, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            const char *ivarName = ivar_getName(ivar);
            NSString *ocStr = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding];
            id obj = [aDecoder decodeObjectForKey:ocStr];
            [self setValue:obj forKey:ocStr];
        }
        free(ivars);
        clazz = [clazz superclass];
    }
}  
@end

版本四的使用

  • 以后使用只需要加入这两行代码就可以了
//归档
- (void)encodeWithCoder:(NSCoder *)aCoder{
    [self encode:aCoder];
}
//解档
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init]) {
        [self decode:aDecoder];
    }
    return self;
}
  • 版本四还不是最简版本,我们可以把这段代码定义为一个宏,这样一句代码就可以搞定归档和解档

第五版

//
//  LGArchive.h
//  RuntimeArchive
//
//  Created by lingo on 2018/2/28.
//  Copyright © 2018年 livefor. All rights reserved.
//

#ifndef LGArchive_h
#define LGArchive_h
#import "NSObject+LGArchive.h"

#define LGArchiveImplementation \
- (instancetype)initWithCoder:(NSCoder *)aDecoder {\
    if (self = [super init]) {\
        [self decode:aDecoder];\
    }\
    return self;\
}\
\
- (void)encodeWithCoder:(NSCoder *)aCoder {\
    [self encode:aCoder];\
}

#endif /* LGArchive_h */

版本五使用

//一句代码
LGArchiveImplementation

总结:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值