ios 使用runtime实现自动解归档

本文详细介绍了如何在Objective-C中实现自定义对象的归档解归档过程,包括常规方法和使用runtime自动处理大量属性的优化方案。通过遵守NSSecureCoding协议,利用runtime获取并编码解码实例变量,实现更灵活的归档解档操作。同时,提供了完整的代码示例,展示了解归档的完整流程。
摘要由CSDN通过智能技术生成

1、解归档应用的对象:自定义对象

2、常规的解归档代码

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
//对自定义对象归档解档
@interface Student : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) double height;
@end
NS_ASSUME_NONNULL_END
#import "Student.h"

//使用解归档对象的类都要遵守 NSCoding 协议,NSSecureCoding 协议继承自 NSCoding,有着比 NSCoding 更加安全的编码和解码。
@interface Student ()<NSSecureCoding>
@end
@implementation Student

/**归档:当一个对象归档(写入)沙盒时用*/
// 当将一个自定义对象保存到文件的时候就会调用该方法
-(void)encodeWithCoder:(NSCoder *)coder {
// 存储自定义对象的属性
    NSLog(@"调用了 encodeWithCoder:方法");
    [coder encodeObject:self.name forKey:@"name"];
    [coder encodeInteger:self.age forKey:@"age"];
    [coder encodeDouble:self.height forKey:@"height"];
}


//归档 - 从沙盒中读取对象时调用
-(instancetype)initWithCoder:(NSCoder *)coder {
    NSLog(@"调用了initWithCoder:方法");
    //注意:在构造方法中需要先初始化父类的方法
    if (self = [super init]) {
        self.name = [coder decodeObjectForKey:@"name"];
        self.age = [coder decodeIntegerForKey:@"age"];
        self.height = [coder decodeDoubleForKey:@"height"];
    }
    return self;
}

3、总结:常规的解归档方法对于自定义对象中的属性个数少是可以的,如果需要进行归解档的属性变得很多时,这种方式就不适用了, 这个时候就需要使用 runtime 来操作。

4、使用 runtime 自动解归档的思路及总结:

  1. 遵守 NSSecureCoding 协议
  2. 导入 #import <objc/runtime.h>
  3. 归档操作
  • a) 获取所有实例变量
  • b) 遍历所有实例变量
  • c) 获取实例变量名
  • d) 通过 KVC 获取 value
  • e) 进行编码
  • f) 手动管理内存
  1. 解档操作
  • a) 获取所有实例变量
  • b) 遍历所有实例变量
  • c) 获取实例变量名
  • d) 进行解码
  • d) 通过 KVC 设置 value
  • f) 手动管理内存

5、代码实现

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

//对自定义对象归档解档
//使用解归档对象的类都要遵守 NSCoding 协议,这里使用的 NSSecureCoding 协议继承自 NSCoding,有着比 NSCoding 更加安全的编码和解码。
@interface Student : NSObject<NSSecureCoding>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) double height;

@end

NS_ASSUME_NONNULL_END

解归档

#import "Student.h"
#import <objc/runtime.h>

@implementation Student

// 归档的时候,系统会使用编码器把当前对象编码成二进制流
-(void)encodeWithCoder:(NSCoder *)coder {
    unsigned int count = 0;
    //获取所有实例变量
    Ivar *ivars = class_copyIvarList([self class], &count);
    //遍历所有实例变量
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        //获取实例变量名
        const char *name = ivar_getName(ivar);
        //将获取到字符类型的实例变量名转换成 字符串类型
        NSString *key = [NSString stringWithUTF8String:name];
        //通过 KVC 获取 value
        id value = [self valueForKey:key];
        //进行编码
        [coder encodeObject:value forKey:key];
    }
    // 因为是 C 语言的东西,不会自动释放,所以这里需要手动释放
    free(ivars);
}

// 解档的时候,系统会把二进制流解码成对象
-(instancetype)initWithCoder:(NSCoder *)coder {
    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 *name = ivar_getName(ivar);
            NSString *key = [NSString stringWithUTF8String:name];
            //原来的解码 decodeObjectForKey:方法 要换成decodeObjectOfClasses:方法
            id value = [coder decodeObjectOfClasses:[NSSet setWithObject:[self class]] forKey:key];
            //通过 KVC 获取 value
            [self setValue:value forKey:key];
        }
        free(ivars);
    }
    return self;
}

//在使用 NSSecureCoding 的时候要实现supportsSecureCoding这个方法,返回 YES
+ (BOOL)supportsSecureCoding {
    return YES;
}

使用的时候

//使用runtime自动解归档
        //拼接存储自定义对象的文件
        NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"Student.plist"];
        Student *s = [[Student alloc]init];
        s.name = @"张三";
        s.age = 21;
        s.height = 168.5;
        
        Student *s2;
        if (@available(iOS 11.0, *)) {
            //归档
            NSError *error;
            NSData *data = [NSKeyedArchiver archivedDataWithRootObject:s requiringSecureCoding:YES error:&error];
            if (error == nil) {  //归档成功后,将数据写入文件中
                [data writeToFile:filePath atomically:YES];
            } else {
                NSLog(@"获取数据失败!!!");
            }
            
            //解档
            NSData *data2 = [[NSData alloc]initWithContentsOfFile:filePath];
            s2 = [NSKeyedUnarchiver unarchivedObjectOfClass:[Student class] fromData:data2 error:nil];
            
        } else {
            //归档(存储数据)
            [NSKeyedArchiver archiveRootObject:s toFile:filePath];
            //接档(获取数据)
            s = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
        }
        
        NSLog(@"name = %@, age = %ld, height = %f",s2.name, s2.age, s2.height);

打印

2022-08-03 17:01:11.926799+0800 Runtime 的相关应用场景[3248:2492131] name = 张三, age = 21, height = 168.500000
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值