iOS 解决 http请求返回json解析 出现-[__NSC
在我们发送了http请求之后,返回responseData数据的时候,我们准备对数据解析成Json格式的时候,报了这样的错误:-[__NSCFDictionary bytes]: unrecognized selector sent to instance xxx,这种情况下,首先检查我们的使用了哪个类发送了http请求,如果发现自己用的AFNetwork 三方库进行发送,那么问题基本上有了答案,就是: 我们把一个NSDictionary对象当成Data 直接 通过NSJsonObject 对象转换为NSDictionary 对象,所以出现了错误。
解决:_NSCFDictionary 你用的AFNetwork, 他已经帮你转化好了,responseObject的数据类型已经是字典了 ,接下来,直接对这个字典进行操作就好了。
顺便普及一下AFNetwork:
概述
我们今天是来深入研究一下这个与我们日常开发密切相关的框架是如何实现的。
这是我对 AFNetworking 整个架构的理解,随后一系列的文章也会逐步分析这些模块。
在这篇文章中,我们有两个问题需要了解:
如何使用 NSURLSession 发出 HTTP 请求
如何使用 AFNetworking 发出 HTTP 请求
NSURLSession
NSURLSession以及与它相关的类为我们提供了下载内容的 API,这个 API 提供了一系列的代理方法来支持身份认证,并且支持后台下载。
使用NSURLSession来进行 HTTP 请求并且获得数据总共有五个步骤:
实例化一个NSURLRequest/NSMutableURLRequest,设置 URL
通过- sharedSession方法获取NSURLSession
在 session 上调用- dataTaskWithRequest:completionHandler:方法返回一个NSURLSessionDataTask
向 data task 发送消息- resume,开始执行这个任务
在 completionHandler 中将数据编码,返回字符串
NSMutableURLRequest*request= [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:@"https://github.com"]];NSURLSession*session= [NSURLSession sharedSession];NSURLSessionDataTask*task= [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSString*dataStr= [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@", dataStr); }];[task resume];
这一段代码可以说是使用NSURLSession发送请求最简单的一段代码了,当你运行这段代码会在控制台看到一坨github首页的 html。
AFNetworking
AFNetworking 的使用也是比较简单的,使用它来发出 HTTP 请求有两个步骤
以服务器的主机地址或者域名生成一个 AFHTTPSessionManager 的实例
调用- GET:parameters:progress:success:failure:方法
AFHTTPSessionManager*manager= [[AFHTTPSessionManager alloc] initWithBaseURL:[[NSURL alloc] initWithString:@"hostname"]];[manager GET:@"relative_url"parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"%@" ,responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@", error); }];
注意:在 iOS9 中,苹果默认全局 HTTPs,如果你要发送不安全的 HTTP 请求,需要在 info.plist 中加入如下键值对才能发出不安全的 HTTP 请求
还有一件事情是要注意的是,AFNetworking 默认接收 json 格式的响应(因为这是在 iOS 平台上的框架,一般不需要 text/html),如果想要返回 html,需要设置acceptableContentTypes
AFNetworking 的调用栈
在这一节中我们要分析一下在上面两个方法的调用栈,首先来看的是AFHTTPSessionManager的初始化方法- initWithBaseURL:
- [AFHTTPSessionManager initWithBaseURL:] - [AFHTTPSessionManager initWithBaseURL:sessionConfiguration:] - [AFURLSessionManager initWithSessionConfiguration:] - [NSURLSession sessionWithConfiguration:delegate:delegateQueue:] - [AFJSONResponseSerializer serializer]// 负责序列化响应- [AFSecurityPolicy defaultPolicy]// 负责身份认证- [AFNetworkReachabilityManager sharedManager]// 查看网络连接情况- [AFHTTPRequestSerializer serializer]// 负责序列化请求- [AFJSONResponseSerializer serializer]// 负责序列化响应
从这个初始化方法的调用栈,我们可以非常清晰地了解这个框架的结构:
其中AFURLSessionManager是AFHTTPSessionManager的父类
AFURLSessionManager负责生成NSURLSession的实例,管理AFSecurityPolicy和AFNetworkReachabilityManager,来保证请求的安全和查看网络连接情况,它有一个AFJSONResponseSerializer的实例来序列化 HTTP 响应
AFHTTPSessionManager有着自己的AFHTTPRequestSerializer和AFJSONResponseSerializer来管理请求和响应的序列化,同时依赖父类提供的接口保证安全、监控网络状态,实现发出 HTTP 请求这一核心功能
初始化方法很好地揭示了 AFNetworking 整个框架的架构,接下来我们要通过分析另一个方法- GET:parameters:process:success:failure:的调用栈,看一下 HTTP 请求是如何发出的:
- [AFHTTPSessionManager GET:parameters:process:success:failure:] - [AFHTTPSessionManager dataTaskWithHTTPMethod:parameters:uploadProgress:downloadProgress:success:failure:]// 返回 NSURLSessionDataTask #1- [AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:]// 返回 NSMutableURLRequest- [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:]// 返回 NSURLSessionDataTask #2- [NSURLSession dataTaskWithRequest:]// 返回 NSURLSessionDataTask #3- [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:] - [AFURLSessionManagerTaskDelegate init] - [AFURLSessionManager setDelegate:forTask:] - [NSURLSessionDataTask resume]
在这里#1#2#3处返回的是同一个 data task,我们可以看到,在#3处调用的方法- [NSURLSession dataTaskWithRequest:]和只使用NSURLSession发出 HTTP 请求时调用的方法- [NSURLSession dataTaskWithRequest:completionHandler:]差不多。在这个地方返回 data task 之后,我们再调用- resume方法执行请求,并在某些事件执行时通知代理AFURLSessionManagerTaskDelegate