MJExtension源码解读

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;
}
原文地址:https://www.cnblogs.com/zhufengshibei/p/10076788.html