• 【iOS】JSONModel的基本使用


    cocoapods的安装和第三方库的配置之前的文章已有涉及,请参考:【iOS】AFNetworking的基本使用【iOS】Masonry库的基本使用

    常规解析JSON数据最基础的方法是使用NSJSONSerialization,见这篇文章【iOS】JSON解析,这样处理数据的方法会有一些麻烦:

    1. 需要很小心地处理Model属性类型与dictionary中的数据对应类型,比如有一个NSURL* url的值,就需要将dict[@"url"]中的NSString类型转化成NSURL类型,很多时候忘记转化就会导致对象类型不一致
    2. 若赋值的地方比较多,每修改一次属性,就需要把所有赋值的地方进行一次整体的更改,工作重复且枯燥
    3. 有时JSON数据如果有遗漏或者变化,不容易发现,比如上边JSON解析这篇文章中,若JSON数据不包含age,通过integerValue方法就会把值赋为0

    使用JSONModel就会自行赋值Model中的属性,相对于常规方法,大大简化了代码量和难度


    JSONModel简介

    简单来说就是调用第三方开源库JSONModel可以简化NSData —— JSON —— Model相互转化这一流程

    当我们向服务器发送网络请求之后,通过JSONModel把请求下来的json数据解析成我们自定义的继承于JSONModelXXXModel类,进而转化成我们熟悉的数据结构赋值给对象,供我们进行访问

    JSONModel不仅使用非常方便,而且还会检查JSON数据的完整性,如果JSON数据不完整会返回nil

    JSONModel还提供了基本的数据类型转换,比如服务器错将数字传成字符串的话,JSONModel也会帮你转换成你期望的类型

    核心数据模型JSONModel类

    来简单分析一下JSONModel.h声明文件的Property Protocol部分

    请添加图片描述

    这些协议里并没有约定任何方法,也不会用来实现,只是作为属性的一种标记

    • 属性添加Ignore协议表示JSONModel不会对这个属性进行解析,使用这种方式来进行本地数据的管理 (属性值可以完全忽略)
      a. 解析时完全忽略ta
      b. 场景:该属性不需要从服务器数据中获取
    {
       @"id":"777",
       @"name":"Jacky",
       @"age":19
    }
    
    @interface MyModel : JSONModel
    
    @property (nonatomic, copy)NSString* id;
    @property (nonatomic, copy)NSString* name;
    @property (nonatomic, assign)NSInteger age;
    
    //一般这个属性都是拼接上去、在本地操作的
    @property (nonatomic, copy)NSString<Ignore>* gender;
    
    @end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • Optional协议表示这个协议是可选的,即JSON数据中如果有这个属性就解析,如果没有就跳过 (属性值可以为空或null)
      a. 某些属性值可以为空
      b. 防止由于服务器返回数据为空导致JSONModel异常(程序崩溃)
    • 可以看到以下两个协议被标记DEPRECATED_ATTRIBUTE,说明已被弃用,下面仅作以记录📝:
      a. ConvertOnDemand协议表示延迟加载 (懒加载), 可以减少在网络读取时的性能消耗
      b. Index协议的作用是可以直接用索引访问该属性(在一个数组中被索引)

    有了这些协议,在声明属性时,我们可以十分容易地设定ta们的解析规则,在JSONModel中,协议除了可以用来规定解析规则外,还可以用来指定 自定义数据类型的解析 ,只是我们需要自己定义一个协议,名称与自定义类名一致, 示例如下:

    @protocol Address : JSONModel
    @end
    
    @interface Address : JSONModel
    @property (nonatomic, strong)NSString* info;
    @end
    
    @interface MyModel : JSONModel
    
    @property (nonatomic, copy)NSString* id;
    @property (nonatomic, copy)NSString* name;
    @property (nonatomic, assign)NSInteger age;
    @property (nonatomic, copy)NSString<Ignore>* gender;
    
    //@property (nonatomic, strong)Address
    * address; @property (nonatomic, strong)NSArray<Address>* address; @end
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如上代码所示,在解析数据时,会直接将address数组中赋值为Address的对象,当然也可以像注释掉的那一行一样直接解析对象

    JSONModel的基本使用

    首先向服务器请求一个JSON数据 — 【iOS】简单的网络请求

    请添加图片描述
    JSON数据来源:知乎日报API分析

    对于Model集合、层级嵌套类型数据

    a. 层级嵌套,Model中嵌套其他Model集合,将被嵌套的集合都写成一个类,且只需在同一个类文件中实现实现即可(例如:StoriesTop_Sories
    b. 包含其他Model集合的属性需要指定层级类型和自身类型(例如:NSArray *)

    @protocol StoriesModel
    @end
    
    @protocol Top_StoriesModel
    @end
    
    @interface StoriesModel : JSONModel
    @property (nonatomic, copy) NSString* image_hue;
    @property (nonatomic, copy) NSString* title;
    @property (nonatomic, copy) NSString* url;
    @property (nonatomic, copy) NSString* hint;
    @property (nonatomic, copy) NSString* id;
    
    @end
    
    @interface Top_StoriesModel : JSONModel
    @property (nonatomic, copy) NSString* image_hue;
    @property (nonatomic, copy) NSString* hint;
    @property (nonatomic, copy) NSString* url;
    @property (nonatomic, copy) NSString* title;
    @property (nonatomic, copy) NSString* id;
    
    @end
    
    @interface TestModel : JSONModel
    
    @property (nonatomic, copy) NSString *date;
    @property (nonatomic, copy) NSArray<StoriesModel> *stories;
    @property (nonatomic, copy) NSArray<Top_StoriesModel > *top_stories;
    
    @end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    设置所有属性可选(遵循Optional协议)

    作用见上述Optional协议说明

    @implementation TestModel
    
    + (BOOL) propertyIsOptional:(NSString *)propertyName {
        return YES;;
    }
    
    @end
    //... ...其余两个类同理
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSON转换为Model

    //LatestStoriesModel* latestStoriesModel = [[LatestStoriesModel alloc] initWithData: data error: nil];
    TestModel* model = [[TestModel alloc] initWithDictionary: responseObject error: nil];
    
    • 1
    • 2

    将Model导出成字典、字符串

    //转化成字典
    NSDictionary* dict = [model toDictionary];
    //转化成字符串
    NSString* string = [model toJSONString];
    
    • 1
    • 2
    • 3
    • 4

    设置下划线自动转驼峰

    a. 自定义把下划线字段解析为驼峰命名属性
    b. 场景:服务器数据返回下划线命名字段可为Model中以驼峰命名的属性相应的赋值
    c. mapperFromUpperCaseToLowerCase 大写转小写

     {
       "order_id": 104,
       "order_product" : @"Product#1",
       "order_price" : 12.95
     }
    
    @interface OrderModel : BaseModel
    @property (nonatomic, strong) NSString *orderId;
    @property (nonatomic, assign) float     orderPrice;
    @property (nonatomic, strong) NSString *orderProduct;
    
    @end
    
    @implementation OrderModel
    + (JSONKeyMapper *)keyMapper
    {
        return [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase];
    }
    
    @end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    实例解析

    - (void)requestLatestStories {
        NSString* jsonData = @"https://news-at.zhihu.com/api/4/news/latest";
        jsonData = [jsonData stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
        
        NSURL* url = [NSURL URLWithString: jsonData];
        NSURLRequest* request = [NSURLRequest requestWithURL: url];
        
        NSURLSession* session = [NSURLSession sharedSession];
        NSURLSessionTask* task = [session dataTaskWithRequest: request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            TestModel* testModel = [[TestModel alloc] initWithData: data error: nil];
    //        NSLog(@"%@", testModel.stories[0].title);
    //        NSLog(@"%@", testModel.stories[0][@"title"]);
    //        StoriesModel* stories = testModel.stories[0];
    //        NSLog(@"%@", stories.title);
            
            NSLog(@"%@", testModel.stories[0]);
        }];
        
        [task resume];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    注意⚠️这里要访问title以及跟它同一层级的数据时,用点语法或键值直接访问会报错,像这样:
    请添加图片描述
    因为在该页面下我们没有事先声明,此处声明一下即可:
    请添加图片描述


    如果将声明写成属性:

    @property (nonatomic, copy) StoriesModel *stories;
    
    • 1
    • 将访问到的testModel.stories[0]赋给刚才声明的属性,因为是在block中进行的操作,所以不能直接使用_stories = testModel.stories[0];

    请添加图片描述

    • 在代码中使用_stories时,编译器将用self->_stories替换代码,并且如果在块内使用它,则该块将捕获self自身而不是stories本身。 警告只是为了确保开发人员了解此行为。
    	self->_stories = testModel.stories[0];
        NSLog(@"%@",self->_stories.title);
    
    • 1
    • 2

    这里涉及一点Block循环引用的知识,编者写过的分析Block文章也只是浅析,现阶段仅简单了解:

    Block循环引用问题 首先什么是循环引用呢? 就是两个对象相互持有,在释放时,相互等待释放,造成死循环谁都释放不了,从而内存泄露。 即block作为self的属性时,又在block内部调用了self的属性和方法,block和self相互持有,那么两者的引用计数都至少是1,都不会被释放。


    • 打印结果:
      请添加图片描述
  • 相关阅读:
    【力扣练习】找一个字符串中不含有重复字符的最长字串的长度
    Java程序中调用Python脚本(兼容Windows与Linux)
    Windows YOLOv5-Libtorch部署
    《canvas》之第1章 canvas概述
    express学习30-多人管理22验证joi
    linux权限管理之SetUID&SetGID
    1-UI自动化-初识selenium,编写第一个selenium脚本
    趣味证书制作生成微信小程序源码
    重入攻击和 DAO 被黑事件
    YOLOV3从数据集到预测
  • 原文地址:https://blog.csdn.net/XY_Mckevince/article/details/133962466