应iOS小组要求,仿写知乎日报需要实现网络请求并解析JSON格式数据,这篇文章仅对基本的网络请求和iOS中的JSON解析作以记录,还涉及到RunLoop的一点小插曲,具体请求过程和原理以后会详细学习!🙏
基本网络流程
- NSURL(Uniform Resource Locator)
URL可直接理解为请求网址,如:https://v0.yiketianqi.com/api?unescape=1&version=v9&appid=72961936&appsecret=m78Z0m2T&city=北京&unescape=1
其格式为:[协议类型]://[服务器器地址]:[端⼝口号]/[资源层级UNIX⽂文件路路径][⽂文件名]?[查询]#[⽚片段ID]
方法URLWithString:
将要请求的地址字符串包装成NSURL对象:
- NSURLRequest
NSURLRequest对象就代表一个请求,
会将NSURL对象以及各种参数设置封装起来,无需设置其他参数时方法+ (instancetype)requestWithURL:(NSURL *)URL;
即可
- NSURLSession
一个session可创建多个请求request,并负责接收、发送和处理请求
整个程序中也可以有多个session
创建session方法(单例sharedSession
):
session会将request封装成Task:
来处理数据
可以看到,如果请求逻辑没那么复杂,也可直接用第二个方法将NSURL封装成session
-
- NSURLSessionTask
用Task类的resume
方法来开启请求
- NSURLSessionTask
简单的网络请求示例
用一个方法dataLoader
封装一下,最后在Controller中调用次此方法
- (void)dataLoader {
self.dict = [[NSDictionary alloc] init];
NSString* urlString = @"https://v0.yiketianqi.com/api?unescape=1&version=v9&appid=72961936&appsecret=m78Z0m2T&city=北京&unescape=1";
//处理字符
//urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLQueryAllowedCharacterSet]];
//创建url
NSURL* url = [NSURL URLWithString: urlString];
//NSURLRequest* request = [NSURLRequest requestWithURL: url];
//创建session
NSURLSession* session = [NSURLSession sharedSession];
//创建task
NSURLSessionTask* task = [session dataTaskWithURL: url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
//解析数据
//self.dict = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &error];
NSLog(@"%@", data);
} else {
NSLog(@"请求出现错误:%@", error);
}
}];
//任务启动,开始请求
[task resume];
}
JSON解析
请求下来的数据是NSData,二进制流
下面用一个类NSJSONSerialization
将该数据转换成对象,用字典接收并打印出来:
self.dict = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &error];
NSLog(@"%@", self.dict);
解析成对象我们仍旧看不懂,这里编码格式是Unicode转义序列这就需要JSON解析(Unicode转中文, … , …),网上有许多JSON解析在线工具
网络请求图示
下面用一张图展示网络请求流程:
JSON格式也可以通过第三方库JSONModel直接在程序中解析,后续将加以学习🫡
网络请求其实可通过第三方库AFNetworking来进行,为我们简化了许多流程,后续将加以学习🤟🏻
有关RunLoop的小插曲
编者一开始其实是在main函数里调用并执行请求的,最后发现command Line Tool并没有显示打印内容,咨询了zxb10学长发现原来是RunLoop的问题
RunLoop 是一个事件循环,负责处理各种事件,包括网络请求的回调。在进行异步网络请求时,通常会使用回调函数或代理方法来处理请求的响应。这些回调函数或代理方法需要在 Run Loop 中执行,才能正确地接收和处理网络请求的响应
- 在
main
函数中,默认情况下并没有启动RunLoop
,因此当网络请求的回调发生时,由于Runloop
没有运行,无法处理这些事件,包括打印输出 - 而在
iOS
项目中,主线程的RunLoop
默认会启动,并且在主RunLoop
运行期间,ta会一直处于运行状态,直到应用程序退出。这意味着主线程的RunLoop
会不断地处理事件,包括网络请求的回调和其他消息
为了解决这个问题,可以在main
函数中创建一个自定义的RunLoop
,并在其中执行网络请求。这样,网络请求的回调就能够在正确的上下文中被执行,从而使打印输出能够正常显示
- (void)dataLoader {
// 创建一个自定义 Runloop,并运行在主线程上
NSRunLoop *runloop = [NSRunLoop mainRunLoop];
self.dict = [[NSDictionary alloc] init];
NSString* urlString = @"https://v0.yiketianqi.com/api?unescape=1&version=v9&appid=72961936&appsecret=m78Z0m2T&city=北京&unescape=1";
//创建url
NSURL* url = [NSURL URLWithString: urlString];
//创建session
NSURLSession* session = [NSURLSession sharedSession];
//创建task
NSURLSessionTask* task = [session dataTaskWithURL: url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
//解析数据
self.dict = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &error];
NSLog(@"%@", self.dict);
} else {
NSLog(@"请求出现错误:%@", error);
}
// 停止自定义 Runloop
CFRunLoopStop([runloop getCFRunLoop]);
}];
//任务启动,开始请求
[task resume];
// 运行自定义 Runloop
[runloop run];
}