最近在學(xué)習(xí)OC Runtime,學(xué)習(xí)嘛最重要的是實踐,所以記錄一下在實踐過程中實現(xiàn)的一個簡單的JSON轉(zhuǎn)Model練習(xí)項目。這個項目主要是利用Runtime機制中,獲取Class的屬性列表和屬性的相關(guān)信息的能力。
一、相關(guān)方法
<code>
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
</code>
<code>
const char *property_getName(objc_property_t property)
</code>
<code>
const char *property_getAttributes(objc_property_t property)
</code>
class_copyPropertyList可以獲取類的屬性列表
property_getName可以獲取屬性名稱
property_getAttributes可以獲取屬性的類型等信息
例如在User類中定義了屬性name和age
@interface User : NSObject
@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
@end
通過下面的代碼
Class clazz = [User class];
u_int count;
objc_property_t *properties = class_copyPropertyList(clazz, &count);
for (int i = 0; i < count; i++) {
const char *propName = property_getName(properties[i]);
const char *propAttr = property_getAttributes(properties[i]);
NSLog(@"%s: %s", propName, propAttr);
}
可以得到如下打印信息:
name: T@"NSString",&,N,V_name
age: T^q,N,V_age
看到這些打印信息,心中已有一些眉目了吧。
二、JSON轉(zhuǎn)Model
轉(zhuǎn)換的過程其實最本質(zhì)的是獲取類的屬性列表,然后和傳遞進來的json字典進行key值對照一一賦值即可。
所以首先我們需要先拿到屬性列表信息。
-(NSMutableDictionary *)keyMapper {
Class clazz = object_getClass(self);//當前類
u_int count;//屬性數(shù)量
objc_property_t *properties = class_copyPropertyList(clazz, &count);//屬性列表
NSMutableDictionary *result = [NSMutableDictionary dictionary];
//解析獲取到的屬性列表
for (int i = 0; i < count; i++) {
// 屬性名稱
const char *key = property_getName(properties[i]);
NSString *propName = [NSString stringWithCString:key encoding:NSUTF8StringEncoding];
// 屬性信息 類似:T@"NSString<Optional>",&,N,V_name
const char *attr = property_getAttributes(properties[i]);
//將字符串分解為基本單元
NSArray *attrArray = [[NSString stringWithCString:attr encoding:NSUTF8StringEncoding] componentsSeparatedByString:@","];
NSArray *firstItems = [[attrArray.firstObject stringByReplacingOccurrencesOfString:@"\"" withString:@""] componentsSeparatedByString:@"@"];
Class type;
if (firstItems.count == 2) {
NSArray *info = [[firstItems.lastObject stringByReplacingOccurrencesOfString:@">" withString:@""] componentsSeparatedByString:@"<"];
//拿到屬性的數(shù)據(jù)類型
if (info.count == 2) {
NSString *typeStr = [info firstObject];
type = NSClassFromString(typeStr);
} else {
type = NSClassFromString(firstItems.lastObject);
}
}
[result setObject: type forKey: propName];
}
return result;
}
當然,服務(wù)器和客戶端可能約定某些字段為Optional,或者客戶端會在本地加一些計算屬性,可以也可以通過添加協(xié)議的方式實現(xiàn),而在獲取屬性列表的時候一并解析出來,最后在賦值時加以簡單的驗證即可。
NSDictionary *keyMapper = [self keyMapper];
for (NSString *key in keyMapper.allKeys) {
// 屬性信息
PropertyInfo *propInfo = [keyMapper objectForKey:key];
// 值
id value = [dict objectForKey:key];
/** 驗證當前屬性是否為Optional */
if (!propInfo.isOptional && value == nil) {
NSString *errMsg = [key stringByAppendingString:@" is can not nil!"];
*error = [NSError errorWithDomain:@"Json Error"
code:1 userInfo:@{@"errMsg": errMsg}];
return self;
}
// 驗證數(shù)據(jù)類型是否正確
if (![value isKindOfClass:propInfo.type]) {
NSString *errMsg = [key stringByAppendingString:@": type inconsistency!"];
*error = [NSError errorWithDomain:@"Json Error"
code:1 userInfo:@{@"errMsg": errMsg}];
return self;
}
if (value) [self setValue:value forKey:key];
}