MJExtentsion:
A fast,convenient and nonintrusive conversion framework between JSON and model.
转换速度快、使用简单方便的字典转模型框架。
我们经常需要从网络上请求Json数据,然后将其转化成我们自己的模型数据,经常使用的框架有JsonModel、YYModel、OptionalJSONModel和MJExtension,所以现在看一下MJExtension的源码,并记录一下。
使用MJExtension
1.pod 'MJExtension'
2.#import "MJExtension.h"
3.开始使用
最简单的使用
创建一个模型:
//User.h @interface User : NSObject @property (nonatomic, copy)NSString *name; @property (nonatomic, copy)NSString *icon; @property (nonatomic, assign)unsigned int age; @property (nonatomic, copy)NSString *height; @property (nonatomic, strong)NSNumber *money; @end
字典转模型:
//ViewController.m NSDictionary *dict = @{ @"name" : @"Jack", @"icon" : @"lufy.png", @"age" : @20, @"height" : @"1.55", @"money" : @100.9 }; // JSON -> User User *user = [User mj_objectWithKeyValues:dict]; NSLog(@"name=%@, icon=%@, age=%u, height=%@, money=%@", user.name, user.icon, user.age, user.height, user.money);
打印结果:
name=Jack, icon=lufy.png, age=20, height=1.55, money=100.9
通过一句简单的代码,就把字典数据转化为了模型数据,非常方便简洁。
对于复杂一点的应用
很多时候json转模型都不是这样简单。有时候会出现模型中嵌套模型或者模型中的属性名和json数据中的key不一致的情况。
下面看一下一个Student类的模型:
//Student.h @interface Student : NSObject @property (nonatomic, copy)NSString *ID; @property (nonatomic, copy)NSString *desc; @property (nonatomic, copy)NSString *nowName; @property (nonatomic, copy)NSString *oldName; @property (nonatomic, copy)NSString *nameChangedTime; @property (nonatomic, strong)Bag *bag; @end
我们看到Student模型中嵌套着Bag这个模型:
//Bag.h @interface Bag : NSObject @property (nonatomic, copy)NSString *name; @property ( nonatomic, assign)double *price; @end
然后我们再看一下它的json数据的数据结构:
NSDictionary *dict = @{ @"id" : @"20", @"description" : @"kids", @"name" : @{ @"newName" : @"lufy", @"oldName" : @"kitty", @"info" : @[ @"test-data", @{ @"nameChangedTime" : @"2013-08" } ] }, @"other" : @{ @"bag" : @{ @"name" : @"a red bag", @"price" : @100.7 } } };
我们可以看到字典数据中是id,而模型中是ID,同样也有desc和description。模型中有newName和oldName这些属性,而字典中这些属性在name字段下面。bag属性也是一样的道理,那么怎么办呢?
我们只需要实现MJExtension中的+ (NSDictionary *)mj_replacedKeyFromPropertyName
方法,在Student.m中#import
然后实现+ (NSDictionary *)mj_replacedKeyFromPropertyName
方法:
//Student.m + (NSDictionary *)mj_replacedKeyFromPropertyName { return @{ @"ID" : @"id", @"desc" : @"description", @"oldName" : @"name.oldName", @"nowName" : @"name.newName", @"nameChangedTime" : @"name.info[1].nameChangedTime", @"bag" : @"other.bag" }; }
这个方法的作用就是在给模型赋值的时候,把右边字段的值赋给模型中左边字段的属性。
转化一下试试:
// JSON -> Student Student *stu = [Student mj_objectWithKeyValues:dict]; // Printing NSLog(@"ID=%@, desc=%@, oldName=%@, nowName=%@, nameChangedTime=%@", stu.ID, stu.desc, stu.oldName, stu.nowName, stu.nameChangedTime); // ID=20, desc=kids, oldName=kitty, nowName=lufy, nameChangedTime=2013-08 NSLog(@"bagName=%@, bagPrice=%d", stu.bag.name, stu.bag.price); // bagName=a red bag, bagPrice=100.700000
这个地方需要关注一个地方就是模型中的nameChangedTime这个属性,在字典中去取值的时候是取name.info[1].nameChangedTime
这个字段的值,这个在后面我们讲核心源码的时候会用到。后面讲源码也会以上面这个为例子来讲,这样比较好理解。
MJExtension核心类简介
MJFoundation
-
这个类中只有一个方法,就是
+ (BOOL)isClassFromFoundation:(Class)c
,这个方法用来判断一个类是否是foundation类及其子类。
MJProperty
这个类非常重要,这个类是对我们类中属性的再封装。
首先会通过runtime的方法去遍历类中的属性:
unsigned int count; objc_property_t *propertyList = class_copyPropertyList([Student class], &count); for (int i = 0; i < count; i++) { objc_property_t property = propertyList[i]; const char *propertyName = property_getName(property); const char *attris = property_getAttributes(property); NSLog(@"%s %s", propertyName, attris); } free(propertyList);
打印结果:
ID T@"NSString",C,N,V_ID desc T@"NSString",C,N,V_desc nowName T@"NSString",C,N,V_nowName oldName T@"NSString",C,N,V_oldName nameChangedTime T@"NSString",C,N,V_nameChangedTime bag T@"Bag",&,N,V_bag
通过char类型的attris字符串我们可以看到,它中间有一个串是表示它是属于哪一个类的,比如NSString,Bag。
通过遍历类的属性,我们得到了objc_property_t类型的属性对象,然后使用这个objc_property_t对象来创建一个对应的MJProperty对象,我们看看MJ大神是怎么做的:
#pragma mark - 缓存 + (instancetype)cachedPropertyWithProperty:(objc_property_t)property { MJExtensionSemaphoreCreate MJExtensionSemaphoreWait MJProperty *propertyObj = objc_getAssociatedObject(self, property); if (propertyObj == nil) { propertyObj = [[self alloc] init]; propertyObj.property = property; objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } MJExtensionSemaphoreSignal return propertyObj; }