为什么80%的码农都做不了架构师?>>>
### - 0x00 写在前面
JSON转Model对于我们iOS开发来说有多重要就不赘述啦,而在Github上比较出名的有明杰老师的MJExtension,YY大神的YYModel,早些时候有Mantle等等。我也试着写了个简单的JSON转Model库RYModel,欢迎各位朋友点星和PR,以下是实现的过程,各位客官轻喷啊。
### - 0x01 定个小目标
我们做这个JSON转Model库虽然简单,但至少要包括以下功能和方法 才能满足简单的日常的开发。
功能 | 支持 |
---|---|
基本类型赋值(bool,int,float...) | |
Model包含其他Model | |
Model属性名和JSON中key不同,下文简称这情况叫Mapping |
方法 | 支持 |
---|---|
JSON->Model [Class ry_modelWithKeyValue] | |
JSON String ->Model [Class ry_modelWithKeyValueString] | |
JSONs -> Models [Class ry_modelsWithKeyValues] | |
Model -> JSON [Class ry_modelToKeyValue] | |
Moldes -> JSONs [Class ry_modelsToKeyValues] |
### - 0x02 分析过程
在分析过程前,建议各位先下载源码RYModel并结合下面的流程图分析
- ①首先我们得给NSObject添加个类别(这样所有的类都可以使用),然后写一个
+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic
给予外部调用入口。 - 然后实例化一个类的对象
- (instancetype)ry_initWithKeyValue:(NSDictionary *)dic
。 - ③得到实例化的对象后,就可以遍历key查找Model是否存在改key
- (NSString *)ry_isExistKey:(NSString *)key
,此方法中也包含了检测mapping
。 - ④得到合法的key后,我们还得判断该key的相对应的类名是否为自定义的类
- (BOOL)ry_isSystemClass:(NSString *)key
如果是自定义的类(Model包含Model的情况),则递归调用回+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic
继续解析,直到得到基本类型或者系统类后才用KVC方法赋值,整个解析流程就此结束。
### - 0x03 JSON->Model 部分源码
- 初始化关键方法:
+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic
{
return [[self alloc] ry_initWithKeyValue:dic];;
}
- (instancetype)ry_initWithKeyValue:(NSDictionary *)dic
{
NSAssert([dic isKindOfClass:[NSDictionary class]], @"此数据为非字典,无法解析");
for (NSString *key in [dic allKeys]) {
NSString *tKey = [self ry_isExistKey:key];
if(tKey.length != 0){
// 存在key
[self ry_setKey:tKey withValue:dic[key]];
}else{
// 不存在key
NSLog(@"不存在该‘%@’字段",key);
}
}
return self;
}
- 判断key是否存在重新
Mapping
的情况,然后再检查key是否能与Model的属性匹配上,关键方法:
/**
是否存在key,如果有则返回key名(映射名)
@param key 字典key
@return 模型属性名
*/
- (NSString *)ry_isExistKey:(NSString *)key
{
const char *aKey;
unsigned int count;
NSDictionary *mapDic;
// Model属性有映射(mapping)
if([self respondsToSelector:@selector(ry_modelMapPropertyNames)]){
mapDic = [self ry_modelMapPropertyNames];
if(mapDic){
for (NSString *tKey in mapDic) {
if([key isEqualToString:mapDic[tKey]]){
return tKey;
}
}
}
}
// Model属性无映射
aKey = [key UTF8String];
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (int i = 0 ; i < count; i++) {
const char *propertyName = property_getName(propertyList[i]);
if(strcmp(propertyName, aKey) == 0){
return [NSString stringWithUTF8String:aKey];
}
}
free(propertyList);
return nil;
}
- 判断key对应的Model属性是否是系统类,如果是用户自定义的类,则取出相应key的类名,类名继续调用
+ (instancetype)ry_modelWithKeyValue:(NSDictionary *)dic
,最后得出系统的类为止,再赋值即可,关键方法:
// 赋值
- (void)ry_setKey:(NSString *)key withValue:(id)value
{
id aValue;
if([self ry_isSystemClass:key]){
// 系统类
aValue = value;
}else{
// 自定义类(model嵌套model)
Class aClass = [self ry_getAttributeClass:key];
if([value isKindOfClass:[NSArray class]]){
// 嵌套的model数据是数组
aValue = [aClass ry_modelsWithKeyValues:value];
}else{
// 嵌套的model数据是字典
aValue = [aClass ry_modelWithKeyValue:value];
}
}
[self setValue:aValue forKey:key];
}
// key是否是系统的类
- (BOOL)ry_isSystemClass:(NSString *)key
{
Class aClass = [self ry_getAttributeClass:key];
if(aClass){
// 判断key的类型是否是系统类
NSBundle *aBundle = [NSBundle bundleForClass:aClass];
if(aBundle == [NSBundle mainBundle]){
// 自定义的类
return NO;
}else{
// 系统类
return YES;
}
}else{
// 基本类型
return YES;
}
}
/**
获取Model属性的类名
eg: T@"RYCourse",&,N,V_course 获取字符串中的'RYCourse'
@param key Model属性对应的
@return Model属性的类名
*/
- (Class)ry_getAttributeClass:(NSString *)key
{
Class aClass;
unsigned int count;
NSRange objRange;
NSRange dotRange;
NSString *aClassStr;
NSMutableString *aAttribute;
const char *att = "";
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (int i = 0 ; i < count; i++) {
const char *propertyName = property_getName(propertyList[i]);
NSString *tStr = [NSString stringWithUTF8String:propertyName];
if([key isEqualToString:tStr]){
att = property_getAttributes(propertyList[i]);
break;
}
}
free(propertyList);
aAttribute = [[NSMutableString alloc] initWithUTF8String:att];
objRange = [aAttribute rangeOfString:@"@"];
if(objRange.location != NSNotFound){
// key是对象,不是基本类型
dotRange = [aAttribute rangeOfString:@","];
aClassStr = [aAttribute substringWithRange:NSMakeRange(3, dotRange.location-1-3)];
aClass = NSClassFromString(aClassStr);
}else{
return nil;
}
return aClass;
}
### - 0x04 最后 至此,JSON->Model就完成了。RYModel有刚刚小目标里提到的方法和功能的源码,有兴趣可以下载看看。如有问题或疑问可以留言或PR。谢谢~