福利->KVC+Runtime获取类/对象的属性/成员变量/方法/协议并实现字典转模型

我们知道,KVC+Runtime可以做非常多的事情。有了这个,我们可以实现很多的效果。

这里来个福利,利用KVC+Runtime获取类/对象的所有成员变量、属性、方法及协议;

并利用它来实现字典转模型。

废话不多说,直接上代码:

1、工具类(其实就是NSObject的一个分类)头文件

 1 #import <Foundation/Foundation.h>
 2
 3 @interface NSObject (YSRuntime)
 4
 5 /**
 6  返回当前类的属性数组
 7
 8  @return 属性数组(如:"name","age","address")
 9  */
10 + (NSArray *)ys_propertiesList;
11
12 /**
13  返回当前类的成员变量数组
14
15  @return 成员变量数组
16  */
17 + (NSArray *)ys_ivarsList;
18
19 /**
20  返回当前类的对象方法数组
21
22  @return 方法数组
23  */
24 + (NSArray *)ys_methodList;
25
26 /**
27  返回当前类的协议数组
28
29  @return 协议数组
30  */
31 + (NSArray *)ys_protocolList;
32
33 /**
34  使用字典创建当前类的对象
35
36  @param dictionary 字典
37
38  @return 当前类的对象
39  */
40 + (instancetype)ys_objectWithDictionary:(NSDictionary *)dictionary;
41
42 /**
43  使用字典数组创建当前类的对象数组
44
45  @param dictArray 字典数组
46
47  @return 当前类的对象数组
48  */
49 + (NSArray *)ys_objectsWithDictionaryArray:(NSArray<NSDictionary *> *)dictArray;
50
51 @end

2、下面我们来实现里面的方法,以后就用它来获取类/对象的属性、成员变量、方法和协议

说明一下:最主要的方法是在objc/runtime.h,这里只是列举了一些,起到抛砖引玉的作用

  1 #import "NSObject+YSRuntime.h"
  2 #import <objc/runtime.h>
  3
  4 @implementation NSObject (YSRuntime)
  5
  6 #pragma mark - 属性数组
  7 const char *propertiesKey = "ys.propertiesList";
  8 + (NSArray *)ys_propertiesList {
  9
 10     // 获取关联对象
 11     NSArray *result = objc_getAssociatedObject(self, propertiesKey);
 12
 13     if (result != nil) {
 14         return result;
 15     }
 16
 17     unsigned int count = 0;
 18     objc_property_t *list = class_copyPropertyList([self class], &count);
 19
 20     NSMutableArray *arrayM = [NSMutableArray array];
 21
 22     for (unsigned int i = 0; i < count; i++) {
 23
 24         objc_property_t pty = list[i];
 25
 26         const char *cName = property_getName(pty);
 27         NSString *name = [NSString stringWithUTF8String:cName];
 28
 29         [arrayM addObject:name];
 30     }
 31
 32     free(list);
 33
 34     // 设置关联对象
 35     objc_setAssociatedObject(self, propertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
 36
 37     return objc_getAssociatedObject(self, propertiesKey);
 38 }
 39
 40 #pragma mark - 私有方法,专门针对字典转模型中的自定义属性,{@"name":@"Dog"},name是属性名,Dog是自定义的模型类,属性名-属性类型
 41 const char *propertiesTypeKey = "ys.propertiesTypeKey";
 42 + (NSArray<NSDictionary *> *)ys_propertiesAndTypeList {
 43
 44     // 获取关联对象
 45     NSArray *result = objc_getAssociatedObject(self, propertiesTypeKey);
 46
 47     if (result != nil) {
 48         return result;
 49     }
 50
 51     unsigned int count = 0;
 52     objc_property_t *list = class_copyPropertyList([self class], &count);
 53
 54     NSMutableArray<NSDictionary *> *arrayM = [NSMutableArray<NSDictionary *> array];
 55
 56     for (unsigned int i = 0; i < count; i++) {
 57
 58         objc_property_t pty = list[i];
 59
 60         const char *cType = property_getAttributes(pty);
 61         NSString *typeStr = [NSString stringWithUTF8String:cType];
 62
 63         if([typeStr containsString:@"\",&"]){
 64             NSRange typeRange = [typeStr rangeOfString:@"\",&"];
 65             NSString *type = [typeStr substringWithRange:NSMakeRange(3, typeRange.location - 3)];
 66
 67             const char *cName = property_getName(pty);
 68             NSString *name = [NSString stringWithUTF8String:cName];
 69
 70             NSDictionary *dict = @{name:type};
 71
 72             [arrayM addObject:dict];
 73         }
 74     }
 75
 76     free(list);
 77
 78     // 设置关联对象
 79     objc_setAssociatedObject(self, propertiesTypeKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
 80
 81     return objc_getAssociatedObject(self, propertiesTypeKey);
 82 }
 83
 84
 85 #pragma mark - 成员变量数组
 86 const char *ivarsKey = "ys.ivarsList";
 87 + (NSArray *)ys_ivarsList {
 88
 89     // 获取关联对象
 90     NSArray *result = objc_getAssociatedObject(self, ivarsKey);
 91
 92     if (result != nil) {
 93         return result;
 94     }
 95
 96     unsigned int count = 0;
 97     Ivar *list = class_copyIvarList([self class], &count);
 98
 99     NSMutableArray *arrayM = [NSMutableArray array];
100
101     for (unsigned int i = 0; i < count; i++) {
102
103         Ivar ivar = list[i];
104
105         const char *cName = ivar_getName(ivar);
106         NSString *name = [NSString stringWithUTF8String:cName];
107
108         [arrayM addObject:name];
109     }
110
111     free(list);
112
113     // 设置关联对象
114     objc_setAssociatedObject(self, ivarsKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);
115
116     return objc_getAssociatedObject(self, ivarsKey);
117 }
118
119 #pragma mark - 方法数组
120 + (NSArray *)ys_methodList {
121
122     unsigned int count = 0;
123     Method *list = class_copyMethodList([self class], &count);
124
125     NSMutableArray *arrayM = [NSMutableArray array];
126
127     for (unsigned int i = 0; i < count; i++) {
128
129         Method method = list[i];
130
131         SEL selector = method_getName(method);
132         NSString *name = NSStringFromSelector(selector);
133
134         [arrayM addObject:name];
135     }
136
137     free(list);
138
139     return arrayM.copy;
140 }
141
142 #pragma mark - 协议数组
143 + (NSArray *)ys_protocolList {
144
145     unsigned int count = 0;
146     __unsafe_unretained Protocol **list = class_copyProtocolList([self class], &count);
147
148     NSMutableArray *arrayM = [NSMutableArray array];
149
150     for (unsigned int i = 0; i < count; i++) {
151
152         Protocol *protocol = list[i];
153
154         const char *cName = protocol_getName(protocol);
155         NSString *name = [NSString stringWithUTF8String:cName];
156
157         [arrayM addObject:name];
158     }
159
160     free(list);
161
162     return arrayM.copy;
163 }
164
165 #pragma mark - 字典 -> 当前类的对象
166 + (instancetype)ys_objectWithDictionary:(NSDictionary *)dictionary{
167
168     // 当前类的属性数组
169     NSArray *list = [self ys_propertiesList];
170
171     NSArray<NSDictionary *> *propertyTypeList = [self ys_propertiesAndTypeList];
172
173     id obj = [self new];
174
175     for (NSString *key in dictionary) {
176
177         if([list containsObject:key]){
178
179             if ([dictionary[key] isKindOfClass:[NSDictionary class]]){ // 属性值为字典
180
181                 for(NSDictionary *dict in propertyTypeList){
182
183                     if([key isEqualToString: [NSString stringWithFormat:@"%@",dict.allKeys.firstObject]]){
184
185                         NSString *classTypeStr = dict[key];
186                         Class class = NSClassFromString(classTypeStr);
187
188                         id objChild = [class ys_objectWithDictionary:dictionary[key]];
189
190                         [obj setValue:objChild forKey:key];
191                     }
192                 }
193
194             }
195             else if([dictionary[key] isKindOfClass:[NSArray<NSDictionary *> class]]){ // 属性值为字典数组
196
197             }
198             else{
199                 [obj setValue:dictionary[key] forKey:key];
200             }
201         }
202     }
203
204     return obj;
205 }
206
207 #pragma mark - 字典数组 -> 当前类的对象数组
208 + (NSArray *)ys_objectsWithDictionaryArray:(NSArray<NSDictionary *> *)dictArray {
209
210     if (dictArray.count == 0) {
211         return nil;
212     }
213
214     // 当前类的属性数组
215     NSArray *list = [self ys_propertiesList];
216
217     NSArray<NSDictionary *> *propertyTypeList = [self ys_propertiesAndTypeList];
218
219     NSMutableArray *arrayM = [NSMutableArray array];
220     for (NSDictionary *dictionary in dictArray) {
221
222         id obj = [self new];
223
224         for (NSString *key in dictionary) {
225
226             if([list containsObject:key]){
227
228                 if ([dictionary[key] isKindOfClass:[NSDictionary class]]){ // 属性值为字典
229
230                     for(NSDictionary *dict in propertyTypeList){
231
232                         if([key isEqualToString: [NSString stringWithFormat:@"%@",dict.allKeys.firstObject]]){
233
234                             NSString *classTypeStr = dict[key];
235                             Class class = NSClassFromString(classTypeStr);
236
237                             id objChild = [class ys_objectWithDictionary:dictionary[key]];
238
239                             [obj setValue:objChild forKey:key];
240                         }
241                     }
242
243                 }
244                 else if([dictionary[key] isKindOfClass:[NSArray<NSDictionary *> class]]){ // 属性值为字典数组
245
246                 }
247                 else{
248                     [obj setValue:dictionary[key] forKey:key];
249                 }
250             }
251         }
252
253         [arrayM addObject:obj];
254     }
255
256     return arrayM.copy;
257 }
258
259 @end

3、和YYModel一样,如果对象的一个属性为另一个自定义对象,那么同样可以起到连转的作用;

4、但比较遗憾的是,也是和YYModel一样,如果有一个属性是数组,又添加了泛型约束,没有取到这个数组的泛型约束。

我记得,YYModel就有这个问题,因为取不到泛型约束,所以当有属性为数组的时候,需要手动指定数组里面的元素类型。

希望各位大神看到后不吝赐教。

5、最后贴出一个小且非常有趣的小东东,里面的key就是我用上面的 “ys_ivarsList” 获取到所有成员变量,然后一级一级找的^_^

1     // 设置随机颜色给Application的statusBar,从此妈妈再也不用担心statusBar的背景色
2     id bgStyle = [[UIApplication sharedApplication] valueForKeyPath:@"statusBar.backgroundView.style"];
3     [bgStyle setValue:[UIColor redColor] forKey:@"backgroundColor"];
1     // frame = (5 649; 55 13)
2     // 修改高德地图和logo,删除和替换自己随意定
3     UIImageView *imgV = [mapView valueForKey:@"logoImageView"]; // 这就就是高德地图的logo
4     imgV.image = nil; //  我们把imageView的图片置为nil
5     [mapView setValue:imgV forKey:@"logoImageView"]; // 哈哈,这个高德地图的logo就不见了,妈妈从此再也不需要担心logo了
时间: 2024-12-09 18:00:00

福利->KVC+Runtime获取类/对象的属性/成员变量/方法/协议并实现字典转模型的相关文章

spring获取类对象,类方法,执行方法

Object object = SpringConfigUtil.getBean(beanName);Method method = ReflectionUtils.findMethod(object.getClass(), methodName, PageVO.class);Map resultMap = (Map) ReflectionUtils.invokeMethod(method, object, pageVO); 原文地址:https://blog.51cto.com/butcher

object-c编程tips-给类对象添加属性

此问题来源于网络封装的一个内容,由于保密的问题,我只叙述我业余的一些想法. 基本思想: 网络请求大家应该都会用一个基类的BaseNetWork, 然后由派生类继承于BaseNetWork,并实现一些特殊的方法. 一般标准的流程是传入block, 当网络请求完毕后,通过block回调回来.这里叙述的关键不是如何实现一个网络请求和回调block的相关内容,而是如何取消请求. 一般基类会实现一个cancelNetWork方法,它根据自己的url进行cancel操作. 举例: 我们使用 DerivedN

PHP面向对象基本概念 类与对象 静态属性 构造/析构方法

1.传统的面向过程和现代面向对象  传统面向过程:是指把将要完成的工作分为若干个步骤一步一步的完成  现代面向对象:是指将要完成的工作拆分为一个一个的对象的任务(功能)每个对象独自完成自己的任务 可以理解为: 一个大扫除 面向过程就是一个人干完大扫除 面向对象就是 一个人扫地 一个人拖地2.面向对象的基本概念  三大特性:封装 继承 多态  类与对象:          张三是一个对象,李四是一个对象 他们都隶属于人这个类         类:描述某一些具有共同特征的物体的概念        

获取JSON对象的属性值

1.问题背景 有一个json对象,其中有键值对,那怎样获取json对象中属性值 2.实现源码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml">

获取JSON对象的属性名称

1.问题背景 一个json对象,是以键值对组成,通过循环json对象,获取json对象中的属性名称 2.实现源码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml&

WPFS数据绑定(要是后台类对象的属性值发生改变,通知在“client界面与之绑定的控件值”也发生改变须要实现INotitypropertyChanged接口)

WPFS数据绑定(要是后台类对象的属性值发生改变,通知在"client界面与之绑定的控件值"也发生改变须要实现INotitypropertyChanged接口) MainWindow.xaml <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="

20.获取测试对象的属性及内容

场景 获取测试对象的内容是前端自动化测试里一定会使用到的技术.比如我们要判断页面上是否显示了一个提示,那么我们就需要找到这个提示对象,然后获取其中的文字,再跟我们的预期进行比较.在webdriver中使用element.attribute()方法可以获取dom元素(测试对象)的属性. 获取测试对象的属性能够帮我们更好的进行对象的定位.比如页面上有很多class都是'btn'的div,而我们需要定位其中1个有具有title属性的div.由于selenium-webdriver是不支持直接使用tit

【Java】+反射1+获取属性/成员变量 的名称及类型

扩展链接:[Java]+反射2+设置属性/成员变量 的值 1.目标类 2.获取目标群类中的属性及属性的类型 3.代码 1 @Test 2 public void testA() { 3 Class clazz = ZGXRequest.class; 4 // step1 获取类中所有的属性 5 Field[] fields = clazz.getDeclaredFields(); 6 for (Field field : fields) { 7 // step2 获取每个属性的类型(以点分割 需

字符串获取类、封装检测数字的方法

1.charAt()方法: 从整个字符串中找到某子字符,即返回指定位置的字符.charAt(str.length).里面的数字最大为字符串长度减一 eg:stringObject.charAt(index):如果参数 index 不在 0 与 string.length 之间,该方法将返回一个空字符串 var str = '妙味课堂'; var str = '妙味课堂'; // alert( str.length ); // alert( str.charAt() ); //默认为第0个 //