在之前暑假写天气预报项目的时候第一次用到了网络请求,当时是用等多个字典和数组存储的信息,在当时写项目的时候就遇到了一个问题,在搜索界面对我想查找的城市天气界面查找完成并保存时,需要将此城市信息传回到主界面,这时信息分散在多个数组和字典里,如果通过传值的话,就需要传好多个参数,十分麻烦,并且容易出错,在当时为了解决这个问题,我就只传回了城市名字,然后在主界面再次网络请求,这样的话在网速不好的时候就容易显示空白页面,并且浪费了内存,在当时也没有想到什么好的解决办法,在学习完JSONModel之后就对此有了很好的解决方法。
首先,JSONModel类是和Masonry一样要从cocoapods里导入,倒入方法:
第一步
在终端打开然后输入:
cd (此时将项目文件夹拖入终端就会显示其路径)
第二步
输入:
touch podFile
此时项目文件夹中会出现一个新的文件podFile,打开文件夹输入以下内容:
platform :ios, '7.0'
target 'JSONModel嵌套' do
pod 'JSONModel'
end
//其中targe后输入项目名称
最后一步,输入
pod install
完成导入。
JSONModel作用:将网络请求到的内容转为model类中的属性,方便我们去使用。
我们先来看看这个类里的代码,首先我们要读取这个API里面的内容:
很清晰看到,这个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的初始化里,我们一般选择用单例来封装,在其中需要用到一些申请网络请求和初始化单例的方法,我们把他们存储在一个管理类里,这就是封装的意义,也符合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传值,这两个传值分别是是否出现错误,进行判断然后传回不同的值。
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(@"请求失败");
}];
}
如果当JSONModel请求到的内容并不是简简单单的几条内容的话,我们该怎么处理呢?
用下面的API去请求数据:
https://news-at.zhihu.com/api/4/news/latest
数组,字典,字符串嵌套在一起,我们该如何正确表达它们的数据呢?
此时就需要声明网络请求中的协议来完成这一操作:
//声明网络请求中的要接收数据的两个协议
@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(@"请求失败");
}];
此时读取的是stories数组的内容,我们再往下一次读取:
由于是model嵌套model所以不能直接调用
我们可以再声明一下需要的被嵌套的model,然后给其赋值,就可以直接调用了。
self->_stories = mainViewModel.stories[0];
NSLog(@"%@",self->_stories.title);