一、介绍

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

  1. //变量结构体
  2. struct objc_ivar {
  3. //变量名称
  4. char * _Nullable ivar_name OBJC2_UNAVAILABLE;
  5.  
  6. //变量类型
  7. char * _Nullable ivar_type OBJC2_UNAVAILABLE;
  8.  
  9. //在地址中的偏移量
  10. int ivar_offset OBJC2_UNAVAILABLE;
  11. #ifdef __LP64__
  12. int space OBJC2_UNAVAILABLE;
  13. #endif
  14. } OBJC2_UNAVAILABLE;
  15.  
  16. //声明了一个变量结构体指针
  17. typedef struct objc_ivar *Ivar;

二、函数

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

  1. //获取类的变量列表
  2. Ivar _Nonnull * _Nullable
  3. class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount) ;
  4.  
  5. //获取变量的名称
  6. const char * _Nullable
  7. ivar_getName(Ivar _Nonnull v);
  8.  
  9. //获取变量的类型
  10. const char * _Nullable
  11. ivar_getTypeEncoding(Ivar _Nonnull v)
  12.  
  13. //通过变量获取对象
  14. id _Nullable
  15. object_getIvar(id _Nullable obj, Ivar _Nonnull ivar);
  16.  
  17. //设置新的变量
  18. 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进行归档和解档

  1. //
  2. // BaseEntity.h
  3. // 运行时
  4. //
  5. // Created by 夏远全 on 2019/11/11.
  6. //
  7.  
  8. #import <Foundation/Foundation.h>
  9. #import <UIKit/UIKit.h>
  10.  
  11. NS_ASSUME_NONNULL_BEGIN
  12.  
  13. @interface BaseEntity : NSObject
  14.  
  15. @end
  16.  
  17. NS_ASSUME_NONNULL_END
  1. //
  2. // BaseEntity.m
  3. // 运行时
  4. //
  5. // Created by 夏远全 on 2019/11/11.
  6. //
  7.  
  8. #import "BaseEntity.h"
  9. #import <objc/runtime.h>
  10.  
  11. @implementation BaseEntity
  12.  
  13. //归档
  14. - (void)encodeWithCoder:(NSCoder *)coder {
  15. NSArray *ivarNames = [self getAllIvarNames];
  16. for (NSString *str_ivar_name in ivarNames) {
  17. //去掉前面的下划线"_"
  18. NSString *key = [str_ivar_name substringFromIndex:];
  19. //归档
  20. [coder encodeObject:[self valueForKey:key] forKey:key];
  21. }
  22. }
  23.  
  24. //解档
  25. - (instancetype)initWithCoder:(NSCoder *)coder {
  26. self = [super init];
  27. if (self) {
  28. NSArray *ivarNames = [self getAllIvarNames];
  29. for (NSString *str_ivar_name in ivarNames) {
  30. //去掉前面的下划线"_"
  31. NSString *key = [str_ivar_name substringFromIndex:];
  32. //解档
  33. [self setValue:[coder decodeObjectForKey:key] forKey:key];
  34. }
  35. }
  36. return self;
  37. }
  38.  
  39. //获取类的所有实例变量
  40. -(NSArray *)getAllIvarNames {
  41.  
  42. //存储所有的变量名称
  43. NSMutableArray *ivarNames = [NSMutableArray array];
  44.  
  45. //拷贝实例变量列表
  46. unsigned int outCount;
  47. Ivar *ivars = class_copyIvarList([self class], &outCount);
  48.  
  49. //遍历实例变量列表
  50. for (int i=; i<outCount; i++) {
  51. Ivar ivar = ivars[i];
  52. const char *ivar_name = ivar_getName(ivar);
  53. NSString *str_ivar_name = [NSString stringWithUTF8String:ivar_name];
  54. [ivarNames addObject:str_ivar_name];
  55. }
  56. return ivarNames;
  57. }
  58.  
  59. @end 

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

  1. //
  2. // FileModel.h
  3. // 运行时
  4. //
  5. // Created by 夏远全 on 2019/11/11.
  6. //
  7.  
  8. #import "BaseEntity.h"
  9.  
  10. NS_ASSUME_NONNULL_BEGIN
  11.  
  12. @interface FileModel : BaseEntity
  13. -(instancetype)initWithFileType:(NSString *)fileType fileName:(NSString *)fileName fileSize:(CGFloat)fileSize;
  14. @end
  15.  
  16. NS_ASSUME_NONNULL_END
  1. //
  2. // FileModel.m
  3. // 运行时
  4. //
  5. // Created by 夏远全 on 2019/11/11.
  6. //
  7.  
  8. #import "FileModel.h"
  9.  
  10. @interface FileModel ()
  11. {
  12. NSString *_fileType; //文件类型,私有成员变量
  13. }
  14. @property (nonatomic, copy) NSString *fileName; //文件名称,私有实例属性
  15. @property (nonatomic, assign) CGFloat fileSize; //文件大小,私有实例属性
  16. @end
  17.  
  18. @implementation FileModel

  19. //初始化
  20. -(instancetype)initWithFileType:(NSString *)fileType fileName:(NSString *)fileName fileSize:(CGFloat)fileSize {
  21.  
  22. self = [super init];
  23. if (self) {
  24. _fileType = fileType;
  25. _fileName = fileName;
  26. _fileSize = fileSize;
  27. }
  28. return self;
  29. }
  30.  
  31. @end

(3)测试并结果显示

  1. -(void)archive {
  2.  
  3. //0:归档对象
  4. FileModel *fileModel = [[FileModel alloc] initWithFileType:@".mp3" fileName:@"单身情歌" fileSize:.f];
  5.  
  6. //1:准备路径
  7. NSString *path = [NSHomeDirectory() stringByAppendingString:@"fileModel.plist"];
  8.  
  9. //2:归档对象
  10. [NSKeyedArchiver archiveRootObject:fileModel toFile:path];
  11. }
  1. -(void)unArchive {
  2.  
  3. //1:解档路径
  4. NSString *path = [NSHomeDirectory() stringByAppendingString:@"fileModel.plist"];
  5.  
  6. //2:反归档对象
  7. FileModel *fileModel = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
  8.  
  9. //3:打印属性
  10. NSString *fileName = ((id(*)(id,SEL))objc_msgSend)(fileModel, @selector(fileName));
  11. CGFloat fileSize = ((CGFloat(*)(id,SEL))objc_msgSend)(fileModel, @selector(fileSize));
  12. NSLog(@"fileName = %@",fileName);
  13. NSLog(@"fileSize = %lf",fileSize);
  14. }
  1. -- ::59.494655+ 运行时[:] fileName = 单身情歌
  2. -- ::59.494847+ 运行时[:] fileSize = 1024.000000

利用Runtime对Ivar实例变量进行共用的归档和解档方式的更多相关文章

  1. runtime之归档和解档

    IOS开发之NSCoding协议(使用runtime)近期学习IOS的runtime库,然后看到之前写的NSCoding协议有点复杂,如果属性少还好,如果100多个属性,则会显得麻烦.下面使用常规方式 ...

  2. 【JAVA】笔记(2)---面向过程与面向对象;类,对象;实例变量,引用;构造方法;

    面向过程与面向对象: 1.面向过程思想的典型栗子是C语言,C语言实现一个程序的流程是:在主函数中一步一步地罗列代码(定义子函数来罗列也是一样的道理),以此来实现我们想要的效果: 2.面向对象思想的典型 ...

  3. 第8.12节 Python类中使用__dict__定义实例变量和方法

    上节介绍了使用实例的__dict__查看实例的自定义属性,其实还可以直接使用__dict__定义实例变量和实例方法. 一. 使用__dict__定义实例变量 语法: 对象名. dict[属性名] = ...

  4. runtime-对成员变量操作应用之归档和返归档

    为了实现归档和返归档,我们要让被归档对象的类接受NSCoding协议并且实现协议里的两个方法 - (void)encodeWithCoder:(NSCoder *)aCoder; - (nullabl ...

  5. runtime第二部分成员变量和属性

    接上一篇 http://www.cnblogs.com/ddavidXu/p/5912306.html 转载来源http://www.jianshu.com/p/6b905584f536 http:/ ...

  6. iOS - 利用runtime加深对基础知识的理解

    利用runtime加深对基础知识的理解 如果对runtime需要学习,可以看这篇,以下仅作为学习笔记,相互交流. runtime的头文件: #import <objc/runtime.h> ...

  7. iOS runtime的应用实例

      一直想弄明白runtime是怎么回事,因为面试的时候这是一道必备问题,但是平时用的机会真的少之又少,我一度以为runtime只是用来装13的利器,没什么卵用.但是随着学习的增多,发现runtime ...

  8. ObjC如何通过runtime修改Ivar的内存管理方式

    ObjC如何通过runtime修改Ivar的内存管理方式 为什么要这么做? 在iOS 9之前,UITableView(或者更确切的说是 UIScrollView)有一个众所周知的问题: propert ...

  9. class_copyIvarList方法获取实例变量问题引发的思考

    在runtime.h中,你可以通过其中的一个方法来获取实例变量,那就是class_copyIvarList方法,具体的实现如下: - (NSArray *)ivarArray:(Class)cls { ...

随机推荐

  1. 微信小程序——表单验证插件WxValidate的二次封装(终极版)

    微信小程序表单验证前面的两篇文章做的效果总感觉都有点不太友好,第一篇里的效果是将错误信息通过对话框形式弹出来,这种形式在web形式下早已经淘汰了:第二篇是一次性全部显示所有的错误,然后3秒后自动消失, ...

  2. 【Java基础】Annotation 的本质和自定义实现

    Java 中注解的实现原理 一.引言 在 Java5 之前,利用 xml 进行配置是各大框架的常规操作,这种方式可以实现松耦合并完成框架中几乎所有需要的配置,但随着项目的扩展,xml 文件本身的内容将 ...

  3. Liu Junqiao:工作中用到的命令以及问题汇总

    工作中用到的命令以及问题汇总 2019-11-29 查看系统运行时间,这个问题是因为我们在阿里云上有个机器,在某一天发现这台机器上有的服务莫名奇妙的停了,然后排查时怀疑机器被重启过用如下如下命令查看了 ...

  4. Download Shuttle Pro mac文件下载器使用指南

    Download Shuttle Pro是适用于macOS的最强大的下载管理器和加速器.它将文件下载分为多个部分,与使用Web浏览器相比,可以提高整体下载速度.使用我们的Pro版本,您可以访问我们的新 ...

  5. Violet 6 杯省选模拟赛 蒲公英

    https://www.luogu.com.cn/problem/P4168 题目 给$n$个数字,有$m$次询问,问$a_l, a_{l+1} , \dots , a_r$的众数是什么, $1\le ...

  6. JQuery解决鼠标单双击冲突问题

    转自链接:https://www.shuzhiduo.com/A/xl560MKrzr/ 在jQuery的事件绑定中,如果元素同时绑定了单击事件(click)和双击事件(dblclick),那么执行单 ...

  7. pandas.apply()函数

    1.介绍 apply函数是pandas里面所有函数中自由度最高的函数.该函数如下: DataFrame.apply(func, axis=0, broadcast=False, raw=False, ...

  8. Spring 框架基础(01):核心组件总结,基础环境搭建

    本文源码:GitHub·点这里 || GitEE·点这里 一.Spring框架 1.框架简介 Spring是一个开源框架,框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 ...

  9. Java描述设计模式(06):建造者模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 基于建造者模式,描述软件开发的流程. 1.代码实现 /** * 基于建造者模式描述软件开发 */ public class C0 ...

  10. 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU量产神器RT-Flash常见问题

    对于MCUBootUtility,RT-Flash工具,有任何使用上的问题,可以在本博客下留言,也可以扫码加入QQ交流群.