我们知道,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了