福利->KVC+Runtime擷取類/對象的屬性/成員變數/方法/協議並實現字典轉模型,kvcruntime
我們知道,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"]; // 這就就是高德地圖的logo4 imgV.image = nil; // 我們把imageView的圖片置為nil5 [mapView setValue:imgV forKey:@"logoImageView"]; // 哈哈,這個高德地圖的logo就不見了,媽媽從此再也不需要擔心logo了