AFNetWorking VS NSURLSession

为了写这篇文章,特意写了前一篇NSURLSession,为了更好的理解,可以先看看前一篇。

本文目的在于讲解AFNetWorking比起原生的NSURLSession发生网络请求,都帮我们做了些什么呢,世人都喜欢AF,那AF到底有多漂亮,下面我会对比原生的NSURLSession揭开AF神秘的面纱,让我们一起目睹她的芳容。

我先梳理一下发起一个网络请求的步骤:

1.构建请求

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];

2.创建会话

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];

NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:queue];

3.创建请求任务

NSURLSessionDataTask *task = [session dataTaskWithRequest:request];

[task resume];

4.请求状态的跟踪(会话状态、数据任务状态、下载状态)

NSURLSessionDelegate  NSURLSessionTaskDelegate  NSURLSessionDataDelegate  NSURLSessionDownloadDelegate

5.请求结果处理

[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]

那么AF在这五部里面都帮我们做了些什么呢,让我们一步一步来扔掉她的扇子,拉下她的纱巾:

1.构建请求

AF中的请求创建源码是这样的

NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];

然后它做对mutableRequest了一系列的设置:

a.属性设置:这个AF还真没有做什么,交给我们自己设置

b.请求头填写:

[mutableRequest setValue:@"" forHTTPHeaderField:@"Accept-Language"];

[mutableRequest setValue:@"" forHTTPHeaderField:@"User-Agent"];

[mutableRequest setValue:@"" forHTTPHeaderField:@"Authorization"];

[mutableRequest setValue:@"" forHTTPHeaderField:@"Content-Type"];

打印了下AF请求的请求头得到:

  Accept-Language = zh-Hans-US;q=1, en-US;q=0.9,

  User-Agent = test2/1.0 (iPhone; iOS 9.3; Scale/3.00)

用这个方法让我们设置Authorization更方便了

- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username password:(NSString *)password{

NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];

NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];

[self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];

}

c.参数填写:

参数填写还是帮我们做了很多复杂的事情,有下面几个:

(1)url编码

(2)请求头里参数的数据类型,参数转码

//默认的

  [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

  [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];

 //json

  [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

  [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];

 //数据列表

  [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];

  [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];

d.上传文件的body拼接

2.创建会话

AF中创建会话的代码是这样的

- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString

parameters:(nullable id)parameters

progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress

success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success

failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString

parameters:(nullable id)parameters

progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress

success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success

failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString

parameters:(nullable id)parameters

constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block

progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress

success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success

failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

从这几个方法的观察,就可以知道AF帮我们做了以下几点:

  a.我们可以不用创建NSURL ,可以直接使用NSString;

  b.不用再自己拼接URL的参数,可以直接通过字典快捷的填写;(而且参数中有中文什么的都可以直接使用,AF里面已经帮你做了url编码)

  c.不用自己到代理方法中获取数据传输的进度,可以直接通过设置block,等待回调就行;

  d.请求结果的回调,只不过原生的NSURLSession分类已经做到这一点了;

  e.不用自己在请求体里拼接复杂的格式化的那一堆东西,上传数据可以通过block直接填写;

在我看来实现这五点已经相当厉害了,长期使用NSURLSession的人光看见AF这样的请求方法就能惊呆了,这减去了平时多少的代码量。

对应NSURLSession的部分代码:

  self.sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

self.operationQueue = [[NSOperationQueue alloc] init];

self.operationQueue.maxConcurrentOperationCount = 1;

self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

3.创建请求任务

AF中创建任务的代码是这样的

__block NSURLSessionDataTask *dataTask = nil;

url_session_manager_create_task_safely(^{

dataTask = [self.session dataTaskWithRequest:request];

});

__block NSURLSessionUploadTask *uploadTask = nil;

url_session_manager_create_task_safely(^{

uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];

});

__block NSURLSessionUploadTask *uploadTask = nil;

url_session_manager_create_task_safely(^{

uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];

});

__block NSURLSessionUploadTask *uploadTask = nil;

url_session_manager_create_task_safely(^{

uploadTask = [self.session uploadTaskWithStreamedRequest:request];

});

__block NSURLSessionDownloadTask *downloadTask = nil;

url_session_manager_create_task_safely(^{

downloadTask = [self.session downloadTaskWithRequest:request];

});

它们都是在一个单列的串行队列中,同步创建的。这个线程的创建代理如下:(有删减)

//AFNetworking中所有的和创建任务相关的事件都放到了一个单例的串行队列中

static dispatch_queue_t url_session_manager_creation_queue() {

static dispatch_queue_t af_url_session_manager_creation_queue;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);

});

return af_url_session_manager_creation_queue;

}

static void url_session_manager_create_task_safely(dispatch_block_t block) {

dispatch_sync(url_session_manager_creation_queue(), block);

}

4.请求状态的跟踪

AF中协议的代码是这样的

//验证服务端证书链

- (void)URLSession:(NSURLSession *)session

didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge

completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler

{

// 创建默认的处理方式,PerformDefaultHandling方式将忽略credential这个参数

NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

__block NSURLCredential *credential = nil;

if (self.sessionDidReceiveAuthenticationChallenge) {

disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);

} else {    // 如果没有实现自定义的验证过程

// 判断challenge的authenticationMethod

if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

// 使用安全策略来验证

if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {

// 如果验证通过,根据serverTrust创建依据

credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

if (credential) {// 有的话就返回UseCredential

disposition = NSURLSessionAuthChallengeUseCredential;

} else {

disposition = NSURLSessionAuthChallengePerformDefaultHandling;

}

} else {

disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;

}

} else {

disposition = NSURLSessionAuthChallengePerformDefaultHandling;

}

}

if (completionHandler) {

completionHandler(disposition, credential);

}

}

//下载数据的获取

- (void)URLSession:(__unused NSURLSession *)session

dataTask:(__unused NSURLSessionDataTask *)dataTask

didReceiveData:(NSData *)data

{

[self.mutableData appendData:data];

}

//把下载文件转移到我们之前设定的地址

- (void)URLSession:(NSURLSession *)session

downloadTask:(NSURLSessionDownloadTask *)downloadTask

didFinishDownloadingToURL:(NSURL *)location

{

NSError *fileManagerError = nil;

self.downloadFileURL = nil;

if (self.downloadTaskDidFinishDownloading) {

self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);

if (self.downloadFileURL) {

[[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];

}

}

}

//数据解析      对请求任务结束的回调block进行线程优化

- (void)URLSession:(__unused NSURLSession *)session

task:(NSURLSessionTask *)task

didCompleteWithError:(NSError *)error

{

NSData *data = nil;

if (self.mutableData) {

data = [self.mutableData copy];

  self.mutableData = nil;

}

  dispatch_async(url_session_manager_processing_queue(), ^{  //异步并行,加快数据的处理时间

    //数据解析

    responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

    //对请求结果的处理进行线程优化

     dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{

if (self.completionHandler) {

self.completionHandler(task.response, responseObject, serializationError);

}

});

  });

}

//上面GCD括号里的参数可能不好理解,两个参数都用了?:来设定,意思是AF的使用者定义了completionGroup就用使用者定义的,没定义就用AF提供的,第二个参数同理。

// 它们的类型dispatch_queue_t completionQueue;    dispatch_group_t completionGroup,下面是这个 url_session_manager_completion_group()的代码:

static dispatch_group_t url_session_manager_completion_group() {

static dispatch_group_t af_url_session_manager_completion_group;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

af_url_session_manager_completion_group = dispatch_group_create();

});

return af_url_session_manager_completion_group;

}

//这个方法创建的队列是一个并行的队列,这加快了数据的处理速度

static dispatch_queue_t url_session_manager_processing_queue() {

static dispatch_queue_t af_url_session_manager_processing_queue;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);

});

return af_url_session_manager_processing_queue;

}

5.请求结果处理

1.AFHTTPResponseSerializer

//这个方法根据在初始化方法中初始化的属性 `acceptableContentTypes` 和 `acceptableStatusCodes` 来判断当前响应是否有效。

- (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error

AFJSONResponseSerializer : AFHTTPResponseSerializer

- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error

{

//1. 验证请求的有效性

if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {

if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {

return nil;

}

}

//2. 解决一个空格引起的 [bug]

id responseObject = nil;

NSError *serializationError = nil;

BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];

if (data.length > 0 && !isSpace) {

//3. 序列化 JSON

responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];

} else {

return nil;

}

//4. 移除 JSON 中的 null

if (self.removesKeysWithNullValues && responseObject) {

responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);

}

if (error) {

*error = AFErrorWithUnderlyingError(serializationError, *error);

}

return responseObject;

}

2.AFXMLParserResponseSerializer

responseObject =[[NSXMLParser alloc] initWithData:data];

3.AFXMLDocumentResponseSerializer

  responseObject = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];

4.AFPropertyListResponseSerializer

  responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];

----------------------持续更新中  如有错误   恳请指正----------------------------

时间: 2024-08-12 02:29:00

AFNetWorking VS NSURLSession的相关文章

iOS开发-AFNetworking/NSURLSession异步处理

相信对于GitHub上著名的AFNetworking API,一定都不陌生.本文是一篇基础使用的心得,老手请绕行. AFNetworking与NSURLSession都是依靠block内的代码进行异步处理来得到网络数据(比如by jSON格式),这样做的好处就是不会阻塞主队列的进程,当block内的代码执行完毕时,进行回调处理,回到主队列.这样当需要得到很多网络数据时,不会让用户在一个空白的页面等待很久,可以把不需要网络数据的部分提前加载,增加用户体验.而且现在NSURLConnection这种

AFNetworking源码分析

来源:zongmumask 链接:http://www.jianshu.com/p/8eac5b1975de 简述 在iOS开发中,与直接使用苹果框架中提供的NSURLConnection或NSURLSession进行网络请求相比,使用AFNetworking会有哪些好处?当同时发起多个网络请求AFNetworking是如何实现并发的,在并发的时候,AFNetworking是如何管理线程的?苹果重构NSURLConnetion推出新的网络加载系统NSURLSession解决了什么问题或者是与NS

step 1 开发方案

在iOS中,常见的发送HTTP请求的方案包括: 苹果官方 名称 说明 NSURLConnection iOS 2.0 推出,用法简单,最古老最经典最直接的一种方案 NSURLSession iOS 7 推出,功能比 NSURLConnection 更加强大 CFNetwork NSURL 的底层,纯C语言,几乎不用 第三方框架 名称 底层 说明 ASIHttpRequest CFNetwork 外号HTTP终结者,功能极其强大,2012年 10 月停止更新,MRC AFNetworking NS

iOS网络编程总结

好长时间没有进行更行了,最近学到AFNetworking框架,在学习完成之后做出一个总结 1.第三方网络的框架 AFNEtworking使用简单,对最新的iOS特性都有很好的支持,对NSURL进行了封装,把系统中比较复杂的方法,整成了比较简单的 ASIHTTPRequest(已经停止更新了) 这个框架对CFNetworking网络框架进行封装,性能.可扩展性比较高,但是已经停止了更新,对最新的iOS的兼容性不是很高,所以用的很少 MKNetworkKit 相对于AF比较轻量级,使用也是相当简单

iOS开发笔记4:HTTP网络通信及网络编程

这一篇主要总结iOS开发中进行HTTP通信及数据上传下载用到的方法.网络编程中常用的有第三方类库AFNetworking或者iOS7开始新推出的NSURLSession,还有NSURLSession的前任NSURLConnection.一般情况下,使用AFNetworking即可满足绝大多数要求,并且更简洁高效. 1.HTTP协议之POST与GET 访问网络数据大部分都要用到HTTP协议,通过HTTP协议向服务器请求数据,再通过HTTP协议获得服务器的响应数据,常见的操作主要是POST与GET,

AFNetworking访问https出现&quot;NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813&quot;

使用AFNetworking来访问http请求非常方便快捷,最简单的请求代码如下: #import "HSTestHTTPSViewController.h" #import <AFNetworking/AFNetworking.h> @interface HSTestHTTPSViewController () @end @implementation HSTestHTTPSViewController - (void)viewDidLoad { [super viewD

AFNetworking访问https出现&quot;NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9806&quot;

在之前一篇博客中<AFNetworking访问https出现"NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813">,而在这篇博客中的这个bug也是非常的类似.出现的场景也是服务端使用了自签名的证书,然后客户端按照<AFNetworking访问https出现"NSURLSession/NSURLConnection HTTP load failed (kC

NSURLSession使用模板和AFNetworking使用模板(REST风格)

1.NSURLSession使用模板 NSURLSession是苹果ios7后提供的api,用来替换 NSURLConnection会话指的是程序和服务器的通信对象//一.简单会话不可以配合会话(get请求) - (void)startRequest { NSString *strURL = [[NSString alloc] initWithFormat:@"http://www.51work6.com/service/mynotes/WebService.php?email=%@&t

[IOS] - 网络模块 NSURLConnection、NSURLSession、AFNetworking

NSURLConnection 是 苹果官网库中自带的简单网络请求的一个类,主要提供了使用URL创建同步和异步请求的方法,NSURLConnection-API 简单介绍一下NSURLConnection 的使用方法 使用NSURLConnection 发送GET同步请求 // 1. 创建URL对象 NSURL *url = [NSURL URLWithString:@"http://mock.allhome.com.cn/mock/5cf76e16de83be001011e63c/0605/h