zoukankan      html  css  js  c++  java
  • MJExtension和JSONModel的使用

    1、使用目的:实现JSON 与model之间的转换。

    我们经常要从服务器上获取JSON数据,将其转化为Model。

    这个过程是无疑是痛苦的。对于JSON数据量相对较少,或者Model里面的属性值较少的情况,处理起来不大费劲。但上架的应用大多是数据量巨大,与后台交互频繁的。更糟的是,后台接口频繁变化,那么维护起来就相当费劲了,因为你每次都要根据新的接口文档来逐一解释数据。往往每次要花你半天时间去修改、调试代码。

     

    2、JSONModel

    JSON -> Dictionary -> Model

     

    以下面的JSON文件为例:

    {
        "data" : [
                        {
                            "name" : "张三",
                            "gender" : "male"
                        },
                        {
                            "name" : "李四",
                            "gender" : "female"
                        },
                        {
                            "name" : "黄五",
                            "gender" : "male"
                        }
                    ]
    }
    

    上述JSON分析:

    Data 就是 数组。数组里每一个元素也是一个字典。

    假设我们tableView里每个cell都要展示一个名字和性别,那么我们需要把一组名字和性别做成一个model来使用。

     

    所以在逻辑上,这个json就是两类mode

    1

     

    {
        "name" : "张三",
        "gender" : "male"
      }
    

     

    2、

    {
    
        "data" : [
                        {
                            "name" : "张三",
                            "gender" : "male"
                        },
                        {
                            "name" : "李四",
                            "gender" : "female"
                        },
                        {
                            "name" : "黄五",
                            "gender" : "male"
                        }
                    ]
    }
    

    一般来说,一个字典就是一个model

    所以,我们要创建的就是这两个model类。

     

    Model.h文件------------------------------------

     

    #import <JSONModel/JSONModel.h>
    
    @protocol OneModel //<NSObject>
    
    @end
    
    @interface OneModel : JSONModel
    
    @property (copy, nonatomic) NSString *name;
    @property (copy, nonatomic) NSString *gender;
    
    
    @end
    
    @interface Model : JSONModel
    
    @property (strong, nonatomic) NSArray<OneModel> *data;
    
    @end
    

     

    Model.m文件------------------------------------

    #import "Model.h"
    
    @implementation OneModel
    
    @end
    
    @implementation Model
    
    @end
    

     

    注意两点:

    1、需要做成多少个model,就该有多少个@interface@implementation

    2、Model里面的属性名必须与json里面的一样。

     

    TableViewController.h

     

     

    #import <UIKit/UIKit.h>
    //@class Model;
    @class MyMJModel;
    @interface TableViewController : UITableViewController
    
    //@property(nonatomic,strong) Model *model;
    @property(nonatomic,strong) MyMJModel *model;
    
    @end
    

    TableViewController.m

     

    #import "TableViewController.h"
    #import "Model.h"
    #import "MyMJModel.h"
    #import "TableViewCell.h"
    #import "Status.h"
    #import <MJExtension/MJExtension.h>
    @interface TableViewController ()
    
    @end
    
    @implementation TableViewController
    static NSString *reuseID = @"reuse";
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self.tableView registerClass:[TableViewCell class] forCellReuseIdentifier:reuseID];
        
        
        [self initData];
    }
    
    #pragma mark - 获取JSON数据
    - (void)initData {
        NSString *path = [[NSBundle mainBundle]pathForResource:@"Model" ofType:@"json"];
        NSData *data = [NSData dataWithContentsOfFile:path];
        if (data) {
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
    
    //        self.model = [[Model alloc]initWithDictionary:dict error:nil];
    
            [MyMJModel mj_setupObjectClassInArray:^NSDictionary *{
                return @{
                         @"users" : @"User"
                         };
            }];
            
            self.model = [MyMJModel mj_objectWithKeyValues:dict];
            
        }
        
        [self objectArray2keyValuesArray];
        
    }
    
    #pragma mark - 隐藏状态栏
    - (BOOL)prefersStatusBarHidden{
        return YES;
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    #pragma mark - Table view data source
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        
        return self.model.users.count;
    }
    
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID forIndexPath:indexPath];
        
        cell.oneModel = self.model.users[indexPath.row];
        return cell;
    }
    
    /**
     *  简单的字典 -> 模型
     */
    - (void) keyValues2object
    {
        // 1.定义一个字典
        NSDictionary *dict = @{
                               @"name" : @"Jack",
                               @"gender" : @"male",
                               };
        
        // 2.将字典转为User模型
        User *user = [User mj_objectWithKeyValues:dict];
        
        // 3.打印User模型的属性
        NSLog(@"name = %@, gender = %@", user.name, user.gender);
    }
    
    /**
     *  复杂的字典 -> 模型 (模型里面包含了模型)
     */
    - (void) keyValues2object2
    {
        // 1.定义一个字典
        NSDictionary *dict = @{
                               @"text" : @"是啊,今天天气确实不错!",
                               
                               @"user" : @{
                                       @"name" : @"Jack",
                                       @"gender" : @"male"
                                       }
                               };
        
        // 2.将字典转为Status模型
        Status *status = [Status mj_objectWithKeyValues:dict];
        
        // 3.打印status的属性
        NSString *text = status.text;
        NSString *name = status.user.name;
        NSString *gender = status.user.gender;
        NSLog(@"text=%@, name=%@, gender=%@", text, name, gender);
    }
    
    /**
     *  字典数组 -> 模型数组
     */
    - (void) keyValuesArray2objectArray
    {
        // 1.定义一个字典数组
        NSArray *dictArray = @[
                               @{
                                   @"name" : @"Jack",
                                   @"gender" : @"male",
                                   },
                               
                               @{
                                   @"name" : @"Rose",
                                   @"gender" : @"female",
                                   }
                               ];
        
        // 2.将字典数组转为User模型数组
        NSArray *userArray = [User mj_objectArrayWithKeyValuesArray:dictArray];
        
        // 3.打印userArray数组中的User模型属性
        for (User *user in userArray) {
            NSLog(@"name=%@, gender=%@", user.name, user.gender);
        }
    }
    
    /**
     *  模型 -> 字典
     */
    - (void) object2keyValues
    {
        // 1.新建模型
        User *user = [[User alloc] init];
        user.name = @"Jack";
        user.gender = @"lufy.png";
        
        Status *status = [[Status alloc] init];
        status.user = user;
        status.text = @"今天的心情不错!";
        
        // 2.将模型转为字典
        //    NSDictionary *dict = [status keyValues];
        NSDictionary *dict = status.mj_keyValues;
        NSLog(@"%@", dict);
    }
    
    /**
     *  模型数组 -> 字典数组
     */
    -(void) objectArray2keyValuesArray
    {
        // 1.新建模型数组
        User *user1 = [[User alloc] init];
        user1.name = @"Jack";
        user1.gender = @"male";
        
        User *user2 = [[User alloc] init];
        user2.name = @"Rose";
        user2.gender = @"female";
        
        NSArray *userArray = @[user1, user2];
        
        // 2.将模型数组转为字典数组
        NSArray *dictArray = [User mj_keyValuesArrayWithObjectArray:userArray];
        NSLog(@"%@", dictArray);
    }
    
    @end
    

    TableViewCell.h

     

    #import <UIKit/UIKit.h>
    //@class OneModel;
    @class User;
    @interface TableViewCell : UITableViewCell
    
    //@property (strong,nonatomic) OneModel *oneModel;
    @property (strong,nonatomic) User *oneModel;
    
    @end
    

    TableViewCell.m

     

    #import "TableViewCell.h"
    #import "Model.h"
    #import "MyMJModel.h"
    
    @implementation TableViewCell
    
    - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
        
        self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
        
        if (self) {
            
        }
        
        return self;
    }
    
    - (void)awakeFromNib {
        [super awakeFromNib];
        // Initialization code
    }
    
    - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
        [super setSelected:selected animated:animated];
        
        // Configure the view for the selected state
    }
    
    //- (void)setOneModel:(OneModel *)oneModel {
    //    _oneModel = oneModel;
    //    self.textLabel.text = oneModel.name;
    //    self.detailTextLabel.text = oneModel.gender;
    //}
    - (void)setOneModel:(User *)oneModel {
        _oneModel = oneModel;
        
        self.textLabel.text = oneModel.name;
        self.detailTextLabel.text = oneModel.gender;
    }
    
    @end
    

    Model.h

     

    #import <JSONModel/JSONModel.h>
    
    @protocol OneModel //<NSObject>
    
    @end
    
    @interface OneModel : JSONModel
    
    @property (copy, nonatomic) NSString *name;
    @property (copy, nonatomic) NSString *gender;
    
    
    @end
    
    @interface Model : JSONModel
    
    @property (strong, nonatomic) NSArray<OneModel> *data;
    
    @end
    

    Model.m

     

     

    #import <JSONModel/JSONModel.h>
    
    @protocol OneModel //<NSObject>
    
    @end
    
    @interface OneModel : JSONModel
    
    @property (copy, nonatomic) NSString *name;
    @property (copy, nonatomic) NSString *gender;
    
    
    @end
    
    @interface Model : JSONModel
    
    @property (strong, nonatomic) NSArray<OneModel> *data;
    
    @end
    

     

     

    步骤总结:

    步骤一:通过CocoaPod安装JSONModel。(不再赘述)

    步骤二:分析JSON数据和业务需求,搭建UITableViewCell、UITableViewController等代码。(不再赘述)

    步骤三:根据分析JSON数据,Model文件,只需写.h文件,.m文件对应类的个数写@implementation@end,其他不用写。

     

    3MJExtention

    MJExtension是一套字典和模型之间互相转换的超轻量级框架

    JSON --> Model、Core Data Model

    JSONString --> Model、Core Data Model

    Model、Core Data Model --> JSON

    JSON Array --> Model Array、Core Data Model Array

    JSONString --> Model Array、Core Data Model Array

    Model Array、Core Data Model Array --> JSON Array

     

     

     

     

     

     

    对比之前JSONModel的例子,我们用MJExtention来实现:

     

     

     

     

     

    MJExtensionruntime原理:

    .runtime介绍

      runtime翻译就是运行时,我们称为运行时机制.在OC中最重要的体现就是消息发送机制.

      1)在C语言中,程序在编译过程中就决定调用哪个函数.

      2)在OC中,编译的时候不会决定调用哪个函数,只要声明了这个函数即可.只有在真正运行的时候,才会去决定调用哪个函数.

    .runtime用法,总结了下大概有以下几种用法.

    1>发送消息

      1)OC调用方法本质就是发送消息,要用消息机制,需要导入<objc/message.h>才可以使用.

      2)objc_msgSend,是只有对象才能发送消息,只能以objc开头.

     

        // 创建person对象

        Person *p = [[Person alloc] init];

     

        // 调用对象方法    [p read];

     

        // 本质:让对象发送消息    objc_msgSend(p, @selector(read));

     

        // 调用类方法的方式:两种

        // 第一种通过类名调用    [Person read];

        // 第二种通过类对象调用

        [[Person class] read];

     

        // 用类名调用类方法,底层会自动把类名转换成类对象调用

        // 本质:让类对象发送消息

        objc_msgSend([Person class], @selector(read));

    下面我画了个消息机制的原理图

     

     

     

    2>交换方法

      个人觉得有点类似于分类或者是类扩展,但是也有区别,它可以保证在系统原有的方法基础上加一些其他方法

    @implementation UIImage (Image)// 加载分类到内存的时候调用
    
    + (void)load
    {
        // 交换方法
        // 获取imageWithName方法地址
        Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
    
        // 获取imageWithName方法地址
        Method imageName = class_getClassMethod(self, @selector(imageNamed:));
    
        // 交换方法地址,相当于交换实现方式    
      method_exchangeImplementations(imageWithName, imageName);
    }
    
    // 不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super.
    // 既能加载图片又能打印
    
    + (instancetype)imageWithName:(NSString *)name
    {
        // 这里调用imageWithName,相当于调用imageName
        UIImage *image = [self imageWithName:name];
    
        if (image == nil) {
            NSLog(@"加载空的图片");
        }
        return image;
    
    }
    
    @end
    

    交换方法的原理图片如下

     

     

     

    3>动态添加方法(performSelector)

    如果一个类有很多方法,加载到内存中生成方法列表需要消耗很多内存,使用动态添加方法可以节省内存.

    @implementation ViewController
    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        // Do any additional setup after loading the view, typically from a nib.
        Person *p = [[Person alloc] init];
    
    
        // 默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。
        // 动态添加方法就不会报错    [p performSelector:@selector(eat)];
    }
    @end
    
    @implementation Person// void(*)()// 默认方法都有两个隐式参数,void eat(id self,SEL sel)
    {
        NSLog(@"%@ %@",self,NSStringFromSelector(sel));
    }
    
    // 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == @selector(eat)) {
            // 动态添加eat方法
            class_addMethod(self, @selector(eat), eat, "v@:");
    
        }
        return [super resolveInstanceMethod:sel];
    }
    
    @end
    

    4>动态添加属性

    原理就是给一个类声明属性,就是给一个类添加关联,而不是把属性的内存添加到这个类的内存.

    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        // Do any additional setup after loading the view, typically from a nib.
        // 给系统NSObject类动态添加属性name
        NSObject *objc = [[NSObject alloc] init];
    
        objc.name = @"cjh";
    
        NSLog(@"%@",objc.name);
    }
    
    @end
    
    // 定义关联的keystatic const char *key = "name";
    
    @implementation NSObject (Property)
    - (NSString *)name
    {
        // 根据关联的key,获取关联的值。
        return objc_getAssociatedObject(self, key);
    }
    
    - (void)setName:(NSString *)name
    {
          objc_setAssociatedObject(self,key,name,OBJC_ASSOCIATION_RETAIN_NONATOMIC);      
    }
    
    @end
    

    5>字典转模型

    MJExtension框架我们应该不陌生,里面字典转模型就是利用了runtime来实现的.

      1)首先,模型设计上,属性我们通常是根据字典来设计的,但是每次都一个一个来写的话很麻烦,我们可以设计一个分类,根据字典生成一个对应的字符串,就是我们想要的模型设计属性.

     

    @implementation NSObject (Log)
    
    // 自动打印属性字符串
    + (void)resolveDict:(NSDictionary *)dict{
    
        // 拼接属性字符串代码
        NSMutableString *strM = [NSMutableString string];
    
        // 1.遍历字典,把字典中的所有key取出来,生成对应的属性代码
        [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
    
             NSString *type;
    
            if ([obj isKindOfClass:NSClassFromString(@"__NSCFString")]) {
    
                type = @"NSString";
    
            }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFArray")]){
    
                type = @"NSArray";
    
            }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")]){
    
                type = @"int";
    
            }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFDictionary")]){
    
                type = @"NSDictionary";
    
            }
    
            // 属性字符串
            NSString *str;
            if ([type containsString:@"NS"]) {
    
                str = [NSString stringWithFormat:@"@property (nonatomic, strong) %@ *%@;",type,key];
    
            }else{
    
                str = [NSString stringWithFormat:@"@property (nonatomic, assign) %@ %@;",type,key];
    
            }
    
            // 每生成属性字符串,就自动换行。
            [strM appendFormat:@"
    %@
    ",str];
        }];
    
        // 把拼接好的字符串打印出来,就好了。
        NSLog(@"%@",strM);
    
    }
    @end
    

    2)利用runtime赋值,注意一下的区别.

     KVC: 遍历字典中所有key,去模型中查找

    runtime: 遍历模型中所有属性,去字典中查找对应value,然后在赋值

    @implementation NSObject (Model)
    
    + (instancetype)modelWithDict:(NSDictionary *)dict
    {
        // 思路:遍历模型中所有属性-》使用运行时
    
        // 0.创建对应的对象
        id objc = [[self alloc] init];
    
        // 1.利用runtime给对象中的成员属性赋值
        unsigned int count;
    
        // 获取类中的所有成员属性(使用copy,不影响内部的ivar)
        Ivar *ivarList = class_copyIvarList(self, &count);
    
    
        for (int i = 0; i < count; i++) {
            // 根据角标,从数组取出对应的成员属性
            Ivar ivar = ivarList[i];
    
            // 获取成员属性名
            NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
    
            // 处理成员属性名->字典中的key
            // 从第一个角标开始截取
            NSString *key = [name substringFromIndex:1];
    
            // 根据成员属性名去字典中查找对应的value
            id value = dict[key];
    
            // 二级转换:如果字典中还有字典,也需要把对应的字典转换成模型
            // 判断下value是否是字典
            if ([value isKindOfClass:[NSDictionary class]]) {
    
                // 获取成员属性类型
               NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
    
                // 裁剪类型字符串
                NSRange range = [type rangeOfString:@"""];
    
               type = [type substringFromIndex:range.location + range.length];
    
                range = [type rangeOfString:@"""];
    
                // 裁剪到哪个角标,不包括当前角标
              type = [type substringToIndex:range.location];
    
                // 根据字符串类名生成类对象
                Class modelClass = NSClassFromString(type);
    
                if (modelClass) { // 有对应的模型才需要转
                    // 把字典转模型
                    value  =  [modelClass modelWithDict:value];
                }
            }
    
            // 三级转换:NSArray中也是字典,把数组中的字典转换成模型.
            // 判断值是否是数组
            if ([value isKindOfClass:[NSArray class]]) {
                // 判断对应类有没有实现字典数组转模型数组的协议
                if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
    
                    // 转换成id类型,就能调用任何对象的方法
                    id idSelf = self;
    
                    // 获取数组中字典对应的模型
                    NSString *type =  [idSelf arrayContainModelClass][key];
    
                    // 生成模型
                   Class classModel = NSClassFromString(type);
    
                    NSMutableArray *arrM = [NSMutableArray array];
    
                    // 遍历字典数组,生成模型数组
                    for (NSDictionary *dict in value) {
                        // 字典转模型
                      id model =  [classModel modelWithDict:dict];
                        [arrM addObject:model];
                    }
    
                    // 把模型数组赋值给value
                    value = arrM;
    
                }
            }
    
            if (value) { // 有值,才需要给模型的属性赋值
    
                // 利用KVC给模型中的属性赋值            
                [objc setValue:value forKey:key];
    
            }
        }
        return objc;
    }
    
    
    @end    
    

    4MJExtentionJSONModel的对比:

    1jsonmodel之间的转换效率对比:

     

     

    运行效率,MJExtensionJSONModel20倍。

    MJExtension > JSONModel > Mantle

     

    2、使用复杂度对比:

    对于复杂的字典,JSONModel处理的原理是通过协议名,去找到名字与之一样的类,而装成一个model的。所以JSONModelMJExtention要多写一个代理,即便这个代理是空的。

    MJExtention要在使用的时候说明数组里每个元素的类是什么类。

     

    所以,在使用复杂度上,各有千秋。

     

    3、耦合度对比:

    MJExtention用的是类别category,而JSONModel在写model类时,则必须继承自JSONModel

    所以在耦合度的对比上,MJExtention占优!

     

    5、特殊情况处理:

    1Objective-C里的关键字,例如,id

    MJExtensionidObjective-C里的关键字,我们一般用大写的ID替换,但是往往服务器给我们的数据是小写的id,这个时候就可以用MJExtension框架里的方法转换一下:

    + (NSDictionary *)mj_replacedKeyFromPropertyName  {
    
        return @{@"ID": @"id"};
    }
    

    JSONModel

    在你的model的.m(实现)文件中:

    + (JSONKeyMapper *)keyMapper  {
    
        return [[JSONKeyMapper alloc] initWithDictionary:@{@"description" : @"bank_description", @"id" : @"bank_id"}];
    
    }
    

    本文的代码可下载下面文件:

    示例代码

  • 相关阅读:
    刷脸支付真的来啦!华为nova3带你玩转酷时代~
    华为nova3发布,将支持华为AI旅行助手
    华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大!
    Duang,HUAWEI DevEco IDE全面升级啦
    HUAWEI HiAI亮相Droidcon柏林2018开发者峰会 开启HiAI海外生态
    华为快应用引擎架构及开发实践
    C#怎样从指定服务器上下载指定文件到本地电脑上
    史上最全的Android的Tab与TabHost讲解
    Android Browser学习二 BrowserActivity 的初始化 --其他重要模块
    Android Browser学习一 application的初始化
  • 原文地址:https://www.cnblogs.com/billios/p/6010166.html
Copyright © 2011-2022 走看看