zoukankan      html  css  js  c++  java
  • 利用Runtime对Ivar实例变量进行共用的归档和解档方式

    一、介绍

    在OC中每一个对象持有的变量都是实例变量,实例变量包括成员变量和属性变量,在runtime中用Ivar表示对象的实例变量。其实,runtime源码中可以看到,Ivar也是一个结构体(基本上在runtime中变量的声明都是用结构体实现的),如下所示,同时苹果为这个结构体另外定义了一个结构体指针。

    //变量结构体
    struct objc_ivar {
        //变量名称
        char * _Nullable ivar_name                               OBJC2_UNAVAILABLE;
        
        //变量类型
        char * _Nullable ivar_type                               OBJC2_UNAVAILABLE;
    
        //在地址中的偏移量
        int ivar_offset                                          OBJC2_UNAVAILABLE;
    #ifdef __LP64__
        int space                                                OBJC2_UNAVAILABLE;
    #endif
    }                                                            OBJC2_UNAVAILABLE;
    
    
    //声明了一个变量结构体指针
    typedef struct objc_ivar *Ivar;

    二、函数

    知道了它的数据结构后,我们再来看看runtime中都提供了哪些常用的关于Ivar的相关函数,如下部分所示:

    //获取类的变量列表
    Ivar _Nonnull * _Nullable
    class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount) ;
    
    //获取变量的名称
    const char * _Nullable
    ivar_getName(Ivar _Nonnull v);
    
    //获取变量的类型
    const char * _Nullable
    ivar_getTypeEncoding(Ivar _Nonnull v) ;
    
    //通过变量获取对象
    id _Nullable
    object_getIvar(id _Nullable obj, Ivar _Nonnull ivar);
    
    //设置新的变量
    object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value);

    三、应用

    在前面的篇章中,介绍过了对property属性获取,并使用property属性可以进行字典与模型的互转。在对象的实例变量的范畴上来看,property明显是少于Ivar的,它只是Ivar的一部分,如果我们使用property进行属性的归档和解档时,会存在数据不完整的问题,此时使用Ivar再合适不过了。不管是公/私有属性、公/私有成员变量,使用Ivar的相关函数都可以拿到。归档和解归档的过程直接放在基类中实现,通过获取所有的ivar进行decodeObjectForKey和encodeObject即可,子类继承自该基类后,就默认全部实现了归档和解归档。实现步骤如下:

    (1)定义基类BaseEntity,获取Ivar列表,对ivar进行归档和解档

    //
    //  BaseEntity.h
    //  运行时
    //
    //  Created by 夏远全 on 2019/11/11.
    //
    
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface BaseEntity : NSObject
    
    @end
    
    NS_ASSUME_NONNULL_END
    //
    //  BaseEntity.m
    //  运行时
    //
    //  Created by 夏远全 on 2019/11/11.
    //
    
    #import "BaseEntity.h"
    #import <objc/runtime.h>
    
    @implementation BaseEntity
    
    //归档
    - (void)encodeWithCoder:(NSCoder *)coder {
        NSArray *ivarNames = [self getAllIvarNames];
        for (NSString *str_ivar_name in ivarNames) {
            //去掉前面的下划线"_"
            NSString *key = [str_ivar_name substringFromIndex:1];
            //归档
            [coder encodeObject:[self valueForKey:key] forKey:key];
        }
    }
    
    //解档
    - (instancetype)initWithCoder:(NSCoder *)coder {
        self = [super init];
        if (self) {
            NSArray *ivarNames = [self getAllIvarNames];
            for (NSString *str_ivar_name in ivarNames) {
               //去掉前面的下划线"_"
               NSString *key = [str_ivar_name substringFromIndex:1];
               //解档
               [self setValue:[coder decodeObjectForKey:key] forKey:key];
            }
        }
        return self;
    }
    
    //获取类的所有实例变量
    -(NSArray *)getAllIvarNames {
        
        //存储所有的变量名称
        NSMutableArray *ivarNames = [NSMutableArray array];
        
        //拷贝实例变量列表
        unsigned int outCount;
        Ivar *ivars = class_copyIvarList([self class], &outCount);
        
        //遍历实例变量列表
        for (int i=0; i<outCount; i++) {
            Ivar ivar = ivars[i];
            const char *ivar_name = ivar_getName(ivar);
            NSString *str_ivar_name = [NSString stringWithUTF8String:ivar_name];
            [ivarNames addObject:str_ivar_name];
        }
        return ivarNames;
    }
    
    @end 

    (2)创建子类FileModel,进行验证

    //
    //  FileModel.h
    //  运行时
    //
    //  Created by 夏远全 on 2019/11/11.
    //
    
    #import "BaseEntity.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface FileModel : BaseEntity
    -(instancetype)initWithFileType:(NSString *)fileType fileName:(NSString *)fileName fileSize:(CGFloat)fileSize;
    @end
    
    NS_ASSUME_NONNULL_END
    //
    //  FileModel.m
    //  运行时
    //
    //  Created by 夏远全 on 2019/11/11.
    //
    
    #import "FileModel.h"
    
    @interface FileModel ()
    {
        NSString *_fileType; //文件类型,私有成员变量
    }
    @property (nonatomic,   copy) NSString *fileName; //文件名称,私有实例属性
    @property (nonatomic, assign) CGFloat  fileSize;  //文件大小,私有实例属性
    @end
    
    @implementation FileModel
    
    //初始化
    -(instancetype)initWithFileType:(NSString *)fileType fileName:(NSString *)fileName fileSize:(CGFloat)fileSize { self = [super init]; if (self) { _fileType = fileType; _fileName = fileName; _fileSize = fileSize; } return self; } @end

    (3)测试并结果显示

    -(void)archive {
        
        //0:归档对象
        FileModel *fileModel = [[FileModel alloc] initWithFileType:@".mp3" fileName:@"单身情歌" fileSize:1024.f];
        
        //1:准备路径
        NSString *path = [NSHomeDirectory() stringByAppendingString:@"fileModel.plist"];
        
        //2:归档对象
        [NSKeyedArchiver archiveRootObject:fileModel toFile:path];
    }
    -(void)unArchive {
        
        //1:解档路径
        NSString *path = [NSHomeDirectory() stringByAppendingString:@"fileModel.plist"];
        
        //2:反归档对象
        FileModel *fileModel = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
        
        //3:打印属性
        NSString *fileName = ((id(*)(id,SEL))objc_msgSend)(fileModel, @selector(fileName));
        CGFloat fileSize = ((CGFloat(*)(id,SEL))objc_msgSend)(fileModel, @selector(fileSize));
        NSLog(@"fileName = %@",fileName);
        NSLog(@"fileSize = %lf",fileSize);
    }
    2019-11-11 17:17:59.494655+0800 运行时[3217:245287] fileName = 单身情歌
    2019-11-11 17:17:59.494847+0800 运行时[3217:245287] fileSize = 1024.000000
  • 相关阅读:
    【ASP.NET 进阶】根据IP地址返回对应位置信息
    【网络文摘】编程的智慧
    【ASP.NET 类库】当你懒得用 Json+Ajax 时,可以试试 AjaxPro
    【iOS 初见】第一个简单的 iOS 应用
    【C#】C# 实现发送手机短信
    【网络文摘】一家公司要了你后,凭什么给你开高工资?
    深入理解Java虚拟机01--概述
    Java虚拟机(五)Java的四种引用级别
    OkHttp3源码详解(六) Okhttp任务队列工作原理
    OkHttp3源码详解(五) okhttp连接池复用机制
  • 原文地址:https://www.cnblogs.com/XYQ-208910/p/11837619.html
Copyright © 2011-2022 走看看