class_copyIvarList方法获取实例变量问题引发的思考
2018-01-02 15:25
946 查看
在runtime.h中,你可以通过其中的class_copyIvarList方法来获取实例变量。具体的实现如下(记得导入头文件<objc/runtime.h>):
如上面代码。其中cls就是你要获取实例变量的类,stuIvarCount用来承载要获取类的实例变量的个数。打印出来的ivarName就是cls的实例变量。接下来对这个方法进行解析:
首先看一下里面的Ivar,先看一下定义:
Ivar是一个叫做objc_ivar的结构体指针,其中的 ifdef判断是判断当前设备是否是64位设备,这里可以延伸出一个方法:
class_copyIvarList的注释如下:
它返回的是一个Ivar的数组,这个数组里面包含了你要查看类的所有实例变量,但是不包括从父类继承过来的。如果你传入的类没有实例变量或者改class为Nil,那么该方法返回的就是NULL,count值也就变成了0。有一点需要注意:你必须使用free()方法将该数组释放。
然后就是通过for循环遍历,通过ivar _ getName拿到ivarName。
以上便是对clas_copyIvarList的介绍。
它还有一个最常用的使用方式(在开发中经常用到的):根据字典或者json字符串转化为model,在网络请求返回数据时经常用到。使用方法就是自己写一个基类的model,然后让项目中用到的model都继承自此基类,基类中的关键代码如下:
这里是把字典转成model,先用class_copyIvar获取该model的所有实例变量,然后通过kvc对属性进行赋值。最终返回model。这里有个点需要注意以下几点:
你的model的属性名称要和服务端返回的数据一致,比如你的model有个属性叫做name,那么你服务端返回的数据字典里面的对应属性也要叫做name,因为这个方法是根据属性从字典里面拿数据的。你也可以做一个映射,让自定义的实例变量名称映射到服务端提供的变量名称。
实现里面有个substringFromIndex:操作,其目的就是把使用该方法拿到的实例变量前面的" _ "去掉。所以你最好使用 @property 进行属性声明,并且不要去修改自动生成的实例变量。(@property = getter + setter + _ ivar,这里的 _ ivar其实就是编译器帮我们生成的实例变量)
接下来你可以尝试去获取UILabel的实例变量列表:
你会发现拿到的结果是这样的:
但是跳转到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里面已经包含:
这样的话在实现里面没有产生实例变量,只是手动实现了getter和setter,所以就不会显示text属性在刚才得到的数组里面了。
至于数组中有UILabel.h里面没有的变量,这个就好理解了,有可能在UILabel.m里面添加了一些实例变量或者在运行时添加了这些实例变量。
除此方法之外,你还可以使用class_copyPropertyList方法,这个是拿到的所有用 @property 声明的属性,包括在.m里面添加的属性(所以打印出来的可能要比真实在.h里面看到的多),具体实现和上面的获取方法类似:
其中的copyPropertyList方法解释如下:
记得使用过后也要调用free去释放数组。(PS:在源代码中暂未找到objc_property结构体的说明)因此,你可以通过使用该方法来实现字典或者json字符串转model操作:
两种方式均可实现model转换操作。
以上便是由class_copyIvarList所引发的思考。
转载请标明来源:http://www.cnblogs.com/zhanggui/p/8177400.html
- (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
相关文章推荐
- iOS开发使用 runtime 方法中的 class_copyIvarList,class_copyMethodList 方法时导致内存泄漏问题
- 运行时简单使用-->获取对象的属性(class_copyIvarList)
- class_copyPropertyList与class_copyIvarList区别,获取属性,使用runtime方法的注意事项
- 获取实例变量和实例方法
- 关于Java的泛型在所声明的对象中如何获取class或者实例的方法的总结
- runtime 系列-2获取类的所有特征(属性,实例变量,方法)
- 多线程环境下对变量的读写操作的原子性问题(一道百度笔试题引发的思考)
- Jquery和JS用外部变量获取Ajax返回的参数值的方法实例(超简单)
- Java中实例方法,实例变量,静态方法,静态变量,final方法重写的问题,覆盖
- class_copyPropertyList和class_copyIvarList的区别
- 问题(怎样在dephi中引用.net中定义的类.方法参数,参数类型为XMLdocument?我要调用此方法并把实例花的xmldocument类型的变量作为参数给它。)
- class_copyIvarList和class_copyMethodList实例
- org.apache.jasper.JasperException: Unable to load class for JSP问题引发的思考
- 关于Java的泛型在所声明的对象中如何获取class或者实例的方法的总结
- 待补充:java类里的私有变量问题,在对象A里有一个对象B,调用对象B的方法获取变量,会有短暂的缓存
- EF(Entity Framework)发生错误”正在创建模型,此时不可使用上下文“的解决办法。 正在创建模型,此时不可使用上下文。如果在 OnModelCreating 方法内使用上下文或如果多个线程同时访问同一上下文实例,可能引发此异常。请注意不保证 DbContext 的实例成员和相关类是线程安全的。 临时解决了这个问题,在Context的构造函数中,禁用了自动初始化:
- 多线程环境下对变量的读写操作的原子性问题(一道百度笔试题引发的思考)
- 关于android studio中使用class.forname()方法动态获取类实例报NO CLASS FOUND异常的几种处理方法
- 黑马程序员:Properties中list(PrintWriter)方法引发的思考:Writer流和OutputStream流的区别
- class中对实例字段进行初始化调用构造方法的问题