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

福利->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了

 

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