class_copyIvarList方法获取实例变量问题引发的思考
在runtime.h中,你可以通过其中的class_copyIvarList方法来获取实例变量。具体的实现如下(记得导入头文件<objc/runtime.h>):
- (NSArray *)ivarArray:(Class)cls {
unsigned int stuIvarCount = 0;
Ivar *ivars = class_copyIvarList(cls, &stuIvarCount);
if (stuIvarCount == 0) {
return nil;
}
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:stuIvarCount];
for (int i = 0;i<stuIvarCount;i++) {
Ivar ivar = ivars[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSLog(@"%@",ivarName);
[arr addObject:ivarName];
}
free(ivars);
return arr;
}
如上面代码。其中cls就是你要获取实例变量的类,stuIvarCount用来承载要获取类的实例变量的个数。打印出来的ivarName就是cls的实例变量。接下来对这个方法进行解析:
首先看一下里面的Ivar,先看一下定义:
/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;
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
}
Ivar是一个叫做objc_ivar的结构体指针,其中的 ifdef判断是判断当前设备是否是64位设备,这里可以延伸出一个方法:
//判断当前设备是否是64位设备,也可以用这个方法判断是否是32位设备
- (BOOL)is64Bit {
#if defined(__LP64__) && __LP64__
return YES;
#else
return NO;
#endif
}
OBJC_EXPORT Ivar _Nonnull * _Nullable
class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_copyIvarList的注释如下:

它返回的是一个Ivar的数组,这个数组里面包含了你要查看类的所有实例变量,但是不包括从父类继承过来的。如果你传入的类没有实例变量或者改class为Nil,那么该方法返回的就是NULL,count值也就变成了0。有一点需要注意:你必须使用free()方法将该数组释放。
然后就是通过for循环遍历,通过ivar _ getName拿到ivarName。
以上便是对clas_copyIvarList的介绍。
它还有一个最常用的使用方式(在开发中经常用到的):根据字典或者json字符串转化为model,在网络请求返回数据时经常用到。使用方法就是自己写一个基类的model,然后让项目中用到的model都继承自此基类,基类中的关键代码如下:
+ (instancetype)zg_modelFromDic:(NSDictionary *)dataDic {
id model = [[self alloc] init];
unsigned int count = 0;
Ivar *ivarsA = class_copyIvarList(self, &count);
if (count == 0) {
return model;
}
for (int i = 0;i < count; i++) {
Ivar iv = ivarsA[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(iv)];
ivarName = [ivarName substringFromIndex:1];
id value = dataDic[ivarName];
[model setValue:value forKey:ivarName];
}
free(ivarsA);
return model;
}
这里是把字典转成model,先用class_copyIvar获取该model的所有实例变量,然后通过kvc对属性进行赋值。最终返回model。这里有个点需要注意以下几点:
- 你的model的属性名称要和服务端返回的数据一致,比如你的model有个属性叫做name,那么你服务端返回的数据字典里面的对应属性也要叫做name,因为这个方法是根据属性从字典里面拿数据的。你也可以做一个映射,让自定义的实例变量名称映射到服务端提供的变量名称。
- 实现里面有个substringFromIndex:操作,其目的就是把使用该方法拿到的实例变量前面的" _ "去掉。所以你最好使用 @property 进行属性声明,并且不要去修改自动生成的实例变量。(@property = getter + setter + _ ivar,这里的 _ ivar其实就是编译器帮我们生成的实例变量)
接下来你可以尝试去获取UILabel的实例变量列表:
[self ivarArray:[UILabel class]]
你会发现拿到的结果是这样的:
(
"_size",
"_highlightedColor",
"_numberOfLines",
"_measuredNumberOfLines",
"_baselineReferenceBounds",
"_lastLineBaseline",
"_previousBaselineOffsetFromBottom",
"_firstLineBaseline",
"_previousFirstLineBaseline",
"_minimumScaleFactor",
"_content",
"_synthesizedAttributedText",
"_defaultAttributes",
"_fallbackColorsForUserInterfaceStyle",
"_minimumFontSize",
"_lineSpacing",
"_layout",
"_scaledMetrics",
"_cachedIntrinsicContentSize",
"_contentsFormat",
"_cuiCatalog",
"_cuiStyleEffectConfiguration",
"_textLabelFlags",
"_adjustsFontForContentSizeCategory",
"__textColorFollowsTintColor",
"_preferredMaxLayoutWidth",
"_multilineContextWidth",
"__visualStyle"
)
但是跳转到UILabel.h,你会发现里面有好多的属性不包含在我们根据该方法得出的属性数组里面,而且使用该方法得到的属性在UILabel.h里面并没有。这个是什么原因呢?
先看一下好多UILabel里面的属性没有在数组里面打印问题:猜想应该是在UILabel.m里面使用了 @dynamic。导致没有自动生成getter、setter和ivar,所以没有在数组里面包含。
@synthsize:如果没有手动实现setter/getter方法那么会自动生成,自动生成_var变量。如果不写,默认生成getter/setter和_var。你也可以使用该关键字自己设置自动变量的名称。
@dynamic告诉编译器:属性的setter/getter需要用户自己实现,不自动生成,而且也不会产生_var变量。
也就是说在UILabel里面虽然有个text的属性,也许在UILabel.m里面已经包含:
@dynamic text;
这样的话在实现里面没有产生实例变量,只是手动实现了getter和setter,所以就不会显示text属性在刚才得到的数组里面了。
至于数组中有UILabel.h里面没有的变量,这个就好理解了,有可能在UILabel.m里面添加了一些实例变量或者在运行时添加了这些实例变量。
除此方法之外,你还可以使用class_copyPropertyList方法,这个是拿到的所有用 @property 声明的属性,包括在.m里面添加的属性(所以打印出来的可能要比真实在.h里面看到的多),具体实现和上面的获取方法类似:
- (NSArray *)propertyArr:(Class)cls {
unsigned count = 0;
objc_property_t *properties = class_copyPropertyList(cls, &count);
if (count == 0) {
return nil;
}
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:count];
for (int i = 0; i < count; i ++) {
objc_property_t property = properties[i];
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)] ;
[arr addObject:propertyName];
}
free(properties);
return arr;
}
其中的copyPropertyList方法解释如下:

记得使用过后也要调用free去释放数组。(PS:在源代码中暂未找到objc_property结构体的说明)因此,你可以通过使用该方法来实现字典或者json字符串转model操作:
+ (instancetype)zg_modelFromDic:(NSDictionary *)dataDic {
id model = [[self alloc] init];
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList([self class], &count);
if (count == 0) {
return model;
}
for (int i = 0;i < count; i++) {
objc_property_t property = properties[i];
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
id value = dataDic[propertyName];
[model setValue:value forKey:propertyName];
}
free(properties);
return model;
}
两种方式均可实现model转换操作。
以上便是由class_copyIvarList所引发的思考。
转载请标明来源:http://www.cnblogs.com/zhanggui/p/8177400.html
class_copyIvarList方法获取实例变量问题引发的思考的更多相关文章
- Java——静态变量/方法与实例变量/方法的区别
静态只能调用静态 非静态: 对象名.方法名 package ti; //通过两个类 StaticDemo.LX4_1 说明静态变量/方法与实例变量/方法的区别. class StaticDemo { ...
- Python staticmethod classmethod 普通方法 类变量 实例变量 cls self 概念与区别
类变量 1.需要在一个类的各个对象间交互,即需要一个数据对象为整个类而非某个对象服务. 2.同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见. 3.有独立的存储区,属于整个类. ...
- System.getProperty()方法获取系统变量
来自我的CSDN博客 今天在阅读JDBC的DriverManager类源码时,看到了这么一句代码: System.getProperty("jdbc.drivers"): ...
- C#关于在返回值为Task方法中使用Thread.Sleep引发的思考
起因 最近有个小伙伴提出了一个问题,就是在使用.net core的BackgroundService的时候,对应的ExecuteAsync方法里面写如下代码,会使程序一直卡在当前方法,不会继续执行,代 ...
- OC中实例变量可见度、setter、getter方法和自定义初始化方法
在对类和对象有一定了解之后,我们进一步探讨实例变量的可见度等相关知识 实例变量的可见度分为三种情况:public(共有),protected(受保护的,默认),private(私有的),具体的不同和特 ...
- oc实例变量初始化方法
1 使用实例setter方法 默认初始化方法 + setName:xxx setAge:xxx 2 使用实例功能类方法,默认初始化方法 + setName:xxx age:xxx3 使用实例初始化方法 ...
- JAVA类与对象(六)------实例变量与类变量的区别,实例方法和类方法的区别
实例变量 实例变量声明在一个类中,但在方法.构造方法和语句块之外: 当一个对象被实例化之后,每个实例变量的值就跟着确定: 实例变量在对象创建的时候创建,在对象被销毁的时候销毁: 实例变量的值应该至少被 ...
- java入门---变量类型&类变量&局部变量&实例变量&静态变量
在Java语言中,所有的变量在使用前必须声明.声明变量的基本格式如下: type identifier [ = value][, identifier [= value] ...] ; ...
- JAVA类与对象---实例变量与类变量的区别,实例方法和类方法的区别
实例变量 实例变量声明在一个类中,但在方法.构造方法和语句块之外: 当一个对象被实例化之后,每个实例变量的值就跟着确定: 实例变量在对象创建的时候创建,在对象被销毁的时候销毁: 实例变量的值应该至少被 ...
随机推荐
- TreeSet(一)--排序
TreeSet(一) 一.TreeSet定义: 与HashSet是基于HashMap实现一样,TreeSet同样是基于TreeMap实现的. 1)TreeSet类概述 ...
- HDU 1394 逆序数 线段树单点跟新 | 暴力
Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java ...
- linux 下配置静态IP
设置静态网络如下[root@bogon network-scripts]# pwd 进入这个路径下 /etc/sysconfig/network-scripts [root@bogon network ...
- Haproxy配置日志显示
安装完haproxy后,日志默认是记录在系统日志下的.为了便于排错以及查看日志,我们需要将haproxy日志剥离出来. 在配置前,我们先来了解下日志的level: local0-local7 16-2 ...
- docker、oci、runc以及kubernetes梳理
容器无疑是近年来云计算中最火热的关键词.随着docker的大热,docker.oci.runc.containerd等等名词也逐渐传播开来.这么多的名词,也容易让人混淆.本文对相关名词和其之间的联系进 ...
- 逐步搭建Lamp环境之rpm软件包管理
Linux中的rpm软件包管理类似于windows下的"xxx软件管家"."xxx电脑管家",其作用主要用于查询软件的安装情况.安装软件.卸载软件. 以下针对这 ...
- 并发与并行的区别 The differences between Concurrency and Parallel
逻辑控制流 在程序加载到内存并执行的时候(进程),操作系统会通过让它和其他进程分时段占用CPU(CPU slices)让它产生自己独占CPU的假象(同时通过虚拟内存让它产生独占内存的假象).在CPU在 ...
- 【最短路】 ZOJ 1544 Currency Exchange 推断负圈
给出 N 种货币 M 条兑换关系 開始时全部的货币S 和有X 块钱 接下来M条关系 A B W1 W2 W3 W4 表示 A->B 所需的手续费为W2块钱 汇率为W1 B->A 所需的手 ...
- hdu 5305 friends
每一次比赛的时候脑子都卡顿, 这次更离谱,我居然二进制枚举边,这么大的复杂度.而且剪不了枝 后来学长说着是道爆搜.搜每一条边.恍然大悟. 仅仅须要剪掉点的度数是奇数的时候,或者他的线上朋友或线下朋友大 ...
- 一起talk C栗子吧(第一百二十六回:C语言实例--statickeyword)
各位看官们,大家好,上一回中咱们说的内置宏的样例.这一回咱们说的样例是:static关键字. 闲话休提.言归正转. 让我们一起talk C栗子吧! 看官们,C语言提供了static关键字.它常常出如今 ...