利用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:];
//归档
[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:];
//解档
[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=; 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:.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);
}
-- ::59.494655+ 运行时[:] fileName = 单身情歌
-- ::59.494847+ 运行时[:] fileSize = 1024.000000
利用Runtime对Ivar实例变量进行共用的归档和解档方式的更多相关文章
- runtime之归档和解档
		IOS开发之NSCoding协议(使用runtime)近期学习IOS的runtime库,然后看到之前写的NSCoding协议有点复杂,如果属性少还好,如果100多个属性,则会显得麻烦.下面使用常规方式 ... 
- 【JAVA】笔记(2)---面向过程与面向对象;类,对象;实例变量,引用;构造方法;
		面向过程与面向对象: 1.面向过程思想的典型栗子是C语言,C语言实现一个程序的流程是:在主函数中一步一步地罗列代码(定义子函数来罗列也是一样的道理),以此来实现我们想要的效果: 2.面向对象思想的典型 ... 
- 第8.12节  Python类中使用__dict__定义实例变量和方法
		上节介绍了使用实例的__dict__查看实例的自定义属性,其实还可以直接使用__dict__定义实例变量和实例方法. 一. 使用__dict__定义实例变量 语法: 对象名. dict[属性名] = ... 
- runtime-对成员变量操作应用之归档和返归档
		为了实现归档和返归档,我们要让被归档对象的类接受NSCoding协议并且实现协议里的两个方法 - (void)encodeWithCoder:(NSCoder *)aCoder; - (nullabl ... 
- runtime第二部分成员变量和属性
		接上一篇 http://www.cnblogs.com/ddavidXu/p/5912306.html 转载来源http://www.jianshu.com/p/6b905584f536 http:/ ... 
- iOS - 利用runtime加深对基础知识的理解
		利用runtime加深对基础知识的理解 如果对runtime需要学习,可以看这篇,以下仅作为学习笔记,相互交流. runtime的头文件: #import <objc/runtime.h> ... 
- iOS runtime的应用实例
		一直想弄明白runtime是怎么回事,因为面试的时候这是一道必备问题,但是平时用的机会真的少之又少,我一度以为runtime只是用来装13的利器,没什么卵用.但是随着学习的增多,发现runtime ... 
- ObjC如何通过runtime修改Ivar的内存管理方式
		ObjC如何通过runtime修改Ivar的内存管理方式 为什么要这么做? 在iOS 9之前,UITableView(或者更确切的说是 UIScrollView)有一个众所周知的问题: propert ... 
- class_copyIvarList方法获取实例变量问题引发的思考
		在runtime.h中,你可以通过其中的一个方法来获取实例变量,那就是class_copyIvarList方法,具体的实现如下: - (NSArray *)ivarArray:(Class)cls { ... 
随机推荐
- 2.华为路由交换技术_TCP/IP参考模型
			1.应用层 2.传输层(主机到主机层) 3.网络层(IPV4) ARP协议:地址解析协议 原理:源终端A想要发送信息给目的终端B,已知B的IP地址,需要获取B的MAC地址.首先它会在局域网广播,一般情 ... 
- mysql5.7中timestam默认值'0000-00-00 00:00:00'报错
			在mysql5.7中设置 timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'会报错: 解决办法: mysql> set sql_mode='NO_A ... 
- Redis分布式缓存实现
			基于redis分布式缓存实现 第一:Redis是什么? Redis是基于内存.可持久化的日志型.Key-Value数据库高性能存储系统,并提供多种语言的API. 第二:出现背景 数据结构(Data S ... 
- Vue 小练习01
			有红, 黄, 蓝三个按钮, 以及一个200X200px的矩形box, 点击不同的按钮, box的颜色会被切换为指定的颜色 <!DOCTYPE html> <html lang=&qu ... 
- go语言之range
			Go 语言范围(Range) Go 语言中 range 关键字用于 for 循环中迭代数组(array).切片(slice).通道(channel)或集合(map)的元素.在数组和切片中它返回元素的索 ... 
- Java描述设计模式(01):单例模式
			本文源码:GitHub·点这里 || GitEE·点这里 一.单例模式 1.概念图解 单例设计模式定义:确保这个类只有一个实例,并且自动的实例化向系统提供这个对象. 2.样例代码 package co ... 
- Oracle 事务ACID的特性
			1.事务对数据库控制操作 事务(Transaction)是用户定义的一个数据库操作序列,是不可分割的一部分的整体.这些操作要么做,要么不做(原子性).事务是对数据库对进行操作的最基本的逻辑单位,他可以 ... 
- LayUi 树形组件tree 实现懒加载模式,展开父节点时异步加载子节点数据
			LayUi框架中树形组件tree官方还在持续完善中,目前最新版本为v2.5.5 官方树形组件目前还不支持懒加载方式,之前我修改一版是通过reload重载实例方法填充子节点数据方式,因为递归页面元素时存 ... 
- Cesium专栏-克里金插值(全国温度为例,附源码下载)
			Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ... 
- python的exe反编译
			目录 python的exe反编译 方法一.使用archive_viewer.py提取pyc 方法二.使用pyinstxtractor.py提取pyc python的exe反编译 驱动人生样本为pyth ... 
