【iOS】—— JSONModel使用以及Manager封装网络请求

JSONModel使用

在之前暑假写天气预报项目的时候第一次用到了网络请求,当时是用等多个字典和数组存储的信息,在当时写项目的时候就遇到了一个问题,在搜索界面对我想查找的城市天气界面查找完成并保存时,需要将此城市信息传回到主界面,这时信息分散在多个数组和字典里,如果通过传值的话,就需要传好多个参数,十分麻烦,并且容易出错,在当时为了解决这个问题,我就只传回了城市名字,然后在主界面再次网络请求,这样的话在网速不好的时候就容易显示空白页面,并且浪费了内存,在当时也没有想到什么好的解决办法,在学习完JSONModel之后就对此有了很好的解决方法。

导入JSONModel类

首先,JSONModel类是和Masonry一样要从cocoapods里导入,倒入方法:
第一步
在终端打开然后输入:

cd (此时将项目文件夹拖入终端就会显示其路径)

【iOS】—— JSONModel使用以及Manager封装网络请求_第1张图片

第二步
输入:

touch podFile

此时项目文件夹中会出现一个新的文件podFile,打开文件夹输入以下内容:

platform :ios, '7.0'
target 'JSONModel嵌套' do
pod 'JSONModel'
end
//其中targe后输入项目名称

最后一步,输入

pod install

完成导入。

JSONModel作用:将网络请求到的内容转为model类中的属性,方便我们去使用。

我们先来看看这个类里的代码,首先我们要读取这个API里面的内容:

【iOS】—— JSONModel使用以及Manager封装网络请求_第2张图片

很清晰看到,这个API里只有三条信息,我们在JSONModel里定义两个个字符串类型和一个int类型的属性进行存储。

#import "JSONModel.h"

@interface TestModel : JSONModel

@property (nonatomic, assign) int status;
@property (nonatomic, copy) NSString *msg;
@property (nonatomic, copy) NSString *latest;

@end

JOSNModel类的存储效果我们一会再看,和Manager相结合才能发挥最大的作用:

Manager封装网络请求的数据

在Manager的初始化里,我们一般选择用单例来封装,在其中需要用到一些申请网络请求和初始化单例的方法,我们把他们存储在一个管理类里,这就是封装的意义,也符合MVC设计的理念。

为什么一定要使用单例?

使用单例模式的变量在整个程序中只需要创建一次,而它生命周期是在它被使用时创建一直到程序结束后进行释放的,类似于静态变量,所以我们需要考虑到它的生命周期,唯一性以及线程安全。

单例代码:

+ (instancetype)sharedManage {
    if (manager == nil) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            manager = [[Manager alloc] init];
        });
    }
    return manager;
}

在Manager中一共要完成两个函数进行请求:

+ (instancetype)sharedManage;
- (void)NetWorkWithData:(DataBlock) dataBlock error:(ErrorBlock) errorBlock;

除了单例的初始化之外,还有网络请求的函数:

网络请求函数

- (void)NetWorkWithData:(DataBlock)dataBlock error:(ErrorBlock)errorBlock {
    NSString *string = [NSString stringWithFormat:@"https://news-at.zhihu.com/api/4/version/ios/2.3.0"];
    string = [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    
    NSURL *url = [NSURL URLWithString:string];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error == nil) {
            TestModel *country = [[TestModel alloc] initWithData:data error:nil];
            dataBlock(country);
        } else {
            errorBlock(error);
        }
    }];
    [dataTask resume];
}

这部分代码,和暑假写天气预报时的代码基本差不多,需要注意的是,这次在完成网络请求的时候,有两个block传值,这两个传值分别是是否出现错误,进行判断然后传回不同的值。

block传值
typedef void (^DataBlock) (TestModel* _Nonnull mainViewModel);
typedef void (^ErrorBlock) (NSError* _Nonnull error);

两个传值分别传正确和错误两种内容,将值传到Controller里面,然后在Controller里判断传回的是正确信息还是错误信息,再做相应的处理。

Controller里的代码:

- (void)test {
    [[Manager sharedManage] NetWorkWithData:^(TestModel * _Nonnull mainViewModel) {
        NSLog(@"%@",mainViewModel.msg);
        NSLog(@"请求成功");
    } error:^(NSError * _Nonnull error) {
        NSLog(@"请求失败");
    }];
}

输出结果:
【iOS】—— JSONModel使用以及Manager封装网络请求_第3张图片

此时把网络断开:
输出结果:
在这里插入图片描述

JSONModel嵌套使用

如果当JSONModel请求到的内容并不是简简单单的几条内容的话,我们该怎么处理呢?

用下面的API去请求数据:

https://news-at.zhihu.com/api/4/news/latest

得到这样的数据:
【iOS】—— JSONModel使用以及Manager封装网络请求_第4张图片

数组,字典,字符串嵌套在一起,我们该如何正确表达它们的数据呢?

此时就需要声明网络请求中的协议来完成这一操作:

//声明网络请求中的要接收数据的两个协议
@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* ga_prefix;
@property (nonatomic, copy) NSString* type;
@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* ga_prefix;
@property (nonatomic, copy) NSString* type;
@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

要注意的一点,在.m文件中要完成一个方法,这个方法用来防止程序崩溃,怎么说呢,就是如果在.h文件里设置的属性,在网络请求里没有获取到的内容,此时app就要崩溃,这个方法就保护了这个问题。

作用是不想因为服务器的某个值没有返回(nil)就使程序崩溃, 我们会加关键字Optional,如果不想每一条属性都添加,我们也可以在.m文件中重写方法

在Controller里完成最后部分:

[[Manager sharedManage] NetWorkWithData:^(TestModel * _Nonnull mainViewModel) {
        NSLog(@"请求成功");
        NSLog(@"%@",mainViewModel.stories[0]);
    } error:^(NSError * _Nonnull error) {
        NSLog(@"请求失败");
    }];

输出结果:
【iOS】—— JSONModel使用以及Manager封装网络请求_第5张图片

此时读取的是stories数组的内容,我们再往下一次读取:

在这里插入图片描述
不管用新办法还是老办法都会报错,对于这个问题,该如何解决?

由于是model嵌套model所以不能直接调用
我们可以再声明一下需要的被嵌套的model,然后给其赋值,就可以直接调用了。
在这里插入图片描述

        self->_stories = mainViewModel.stories[0];
        NSLog(@"%@",self->_stories.title);

输出结果:
【iOS】—— JSONModel使用以及Manager封装网络请求_第6张图片

你可能感兴趣的:(ios)