来源:zongmumask

链接:http://www.jianshu.com/p/8eac5b1975de

简述

在iOS开发中,与直接使用苹果框架中提供的NSURLConnection或NSURLSession进行网络请求相比,使用AFNetworking会有哪些好处?当同时发起多个网络请求AFNetworking是如何实现并发的,在并发的时候,AFNetworking是如何管理线程的?苹果重构NSURLConnetion推出新的网络加载系统NSURLSession解决了什么问题或者是与NSURLConnection相比NSURLSession有好些好处?上面问题的答案会贯穿在这篇文章中(本篇文章只涉及了与操作队列,多线程相关的分析)。

不用网络框架进行网络请求

NSURLConnection的简单使用(下面的代码均只为了演示,更详细的使用方法请自行谷歌)

NSURLConnection提供了两个类方法用于发起同步或异步请求,对于异步请求来说必然是在子线程中发起,若在主线程中发起异步网络请求会造成主线程阻塞,界面无响应,这就涉及到多线程编程。但多线程编程是一门非常细致的活,要考虑很多的问题,比如线程的生命周期,多线程资源竞争,加锁,避免死锁,稍不留意就会踩到坑里。好在苹果的接口使用极其方便,你甚至不需要理解多线程就能轻松使用上多线程了,发起异步请求的接口内部已经实现了多线程相关的操作。越是高级的接口,其隐藏的细节就越多,对于开发者来说当然是很方便,但若还能对其实现有所理解那就更完美了。当子线程发起了异步请求后会阻塞以等待网络响应,那应该由谁来处理网络响应呢?苹果提供了两种方式,一种是block,提供一个处理响应的block回调。一种是代理,使用代理的话就必须实现NSURLConnectionDelegate这个协议。你只需要在有网络请求的UIViewController中调用NSURLConnection提供的类方法就可以了。但如果你的项目中有不止一个UIViewController或者有的UIViewController中都不止一个请求的话,你就需要在每一个有网络请求的UIViewController中这样写。这样写会有什么问题呢?首先会造成软件结构不清晰,没有剥离出网络层,其次没有实现网络请求统一管理,无法实现取消所有网络请求等功能,再有会出现很多重复的代码,没有让公用功能形成模块,进行复用。当然为了解决这些问题你也可以自己实现自己的网络框架。

使用NSURLConnection版本的AFNetworking

使用网络框架的好处在于可以将分散在各个视图控制器中的网络请求统一起来模块化形成网络层,降低与数据层和表现层的耦合。AFNetworking就做了这样的工作。网络上已经有很多分析基于NSURLConnection实现的AFNetworking 2.x的源代码,这里只简单说一下其实现整个流程,想要深入了解的请谷歌或查看其源代码。首先AFNetworking要解决实现接口统一和所有网络请求统一管理的问题。NSURLConnection发起有两种方式发起请求,分别是设置响应block和代理,那采用哪种方式能够实现网络请求统一管理呢?block肯定不行,因为传入响应回调block的[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]方法会立即执行。那么只能使用设置响应代理这种方式了。具体实现是将网络请求继承于NSOperation,生产网络请求,然后再将这个网络请求加入操作队列里,剩下的所有与线程相关的操作都由这个操作队列去实现。越高级易容的接口就隐藏越多的实现细节,NSOperation隐藏了线程相关的所有细节,使得开发者只需关心构建什么样的操作。AFNetworking甚至隐藏了操作,操作队列的概念,使得开发者只需关心如何设置网络相关的请求参数,响应回调等,而不用再关心当多个网络请求如何进行统一管理,这些都已经由AFNetworking内部的操作队列属性完成了。那需要为每个请求创建一个线程来发起请求吗?基于NSURLConnection的AFNetworking是只创建了一条线程来发起所有请求并阻塞以等待响应。

重构推出的NSURLSession解决了NSURLConnection哪些问题

从2013年苹果发布重构后的加载系统NSURLSession,AFNetworking这个最受欢迎的网络框架也随之发布了基于NSURLSession的实现版本。这篇文章从 NSURLConnection 到 NSURLSession对NSURLSession的使用做了介绍。既然是对NSURLConnection进行的重构,那一定是解决了NSURLConnection存在的一些问题。苹果的接口以及其简单的形式呈现给开发者,尽量把复杂,容易出错的地方以简单的接口暴露出来,这使得用户能够参考文档很快上手,但这也有一些弊端,就是开发者对其中的原理不是很了解。越高级的接口越容易使用,但其隐蔽的实现细节就越多。相信在进行多线程编程的时候,很多开发者遇到过各种各样的坑,但在iOS平台上,苹果推出了GCD,NSperation等一系列接口可以让开发者完全可以只关注业务的实现,这些接口内部已经替开发者管理好了线程的创建,销毁,多线程资源竞争等需要开发者费很多精力的事情,甚至开发者不用对多线程理解透彻都能把多线程用得得心应手。NSURLConnection已经隐藏了线程相关的操作,已经给开发者减轻了很多负担。但NSURLConnectoin只隐藏了单个网络请求的线程的相关操作,并没有提供接口来解决多个网络请求时多个线程的管理问题,譬如当有多个网络请求时是否应该使用线程池来避免不停创建与销毁线程(这个可以有NSOperationQueue很好的解决)。并且NSURLConnection不是基于HTTP/2协议的,若使用NSURLConnection发起请求则每次请求都需要经过三次握手过程,可见NSURLConnection确实有很多可以优化的地方(我只发现这些)。NSURLConnection存在的无法将多个请求关联起来的问题已经很好的由AFNetworking解决了,所以推出的NSURLSession可以说是借鉴了AFNetworking的思想,并且可以从AFNetworking的源代码中很容易的看出来。基于NSURLConnection的AFNetworking需要让网络请求继承与NSOperation,然后再将该生成的网络请求加入操作队列中,但基于NSURLSessioin的AFNetworking只需要创建一个网络请求任务就可以了,原因在于,NSURLSession内部已经维护了两个操作队列,一个是处理session的相关回调,一个是处理响应相关的回调,所以说NSURLSession是借鉴了AFNetworking的继承于NSOperation和用单线程发起并等待响应的思想(这个会在后面给出证明)。

基于NSURLSession的AFNetworking的源码分析

下图是基于NSURLSession的AFNetworking的UML图(只为展示类之间的关联关系,并没有给出每个类的所有属性和方法):

从该类图已经能够明白AFNetworking整个的工作流程。

下面进行代码分析,使用AFHTTPSessionManager进行网络请求的示例代码(具体的使用请参考AFNetworking的README文档):

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

[manager GET:URL parameters:nil progress:nil success:^(NSURLSessionDataTask *_Nonnulltask, id _NullableresponseObject) {

dispatch_async(dispatch_get_main_queue(), ^{

//将子线程从网络拉取的数据用于主线程刷新视图

});

}failure:^(NSURLSessionDataTask*_Nullabletask,NSError*_Nonnullerror) {

}];

通过查看manager方法代码可以看到其实现最终是调用了父类AFURLSessionManager的initWithSessionConfiguration:方法,该方法代码片段如下:

self.sessionConfiguration = configuration;

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

self.operationQueue.maxConcurrentOperationCount = 1;

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

self.responseSerializer = [AFJSONResponseSerializer serializer];

可以看到AFNetworking对数据的解析方式默认是json解析。

这里设置的代理操作队列最大的并发操作数为1是让所有请求的发起和等待网络响应均在同一条线程中执行,而不用为每一个请求都新建一条线程,这样节约了很多资源。

在响应到达后会执行AFURLSessionManager的NSURLSessionDataDelegate协议的方法,[AFURLSessioinManager URLSession:dataTask:didReceiveData:]用于查找对应的响应代理,并将后续的数据处理如数据拼接转交给该代理。该方法的实现代码如下:

AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];

[delegate URLSession:session dataTask:dataTask didReceiveData:data];

if (self.dataTaskDidReceiveData) {

self.dataTaskDidReceiveData(session, dataTask, data);

}

在数据比较大时,改方法可能会多次执行。

当数据传输完成后会调用[AFURLSessioinManager URLSession:task:didCompleteWithError],该方法用于让对应的代理执行NSURLSessionTaskDelegate协议中的方法,并将该代理对象从字典中移除,源代码如下:

AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

// delegate may be nil when completing a task in the background

if (delegate) {

[delegate URLSession:session task:task didCompleteWithError:error];

[self removeDelegateForTask:task];

}

if (self.taskDidComplete) {

self.taskDidComplete(session, task, error);

}

随后代理执行URLSession:task:didCompleteWithError:,该方法把数据放到另一个由静态方法生成的url_session_manager_processing_queue操作队列中做数据解析,如json解析,并将解析后的数据回传到主线程或者你自己生成的操作队列里,通过通知中心将请求完成的消息传递到主线程去(后面会写一篇文章介绍通知中心的实现原理,并写一个类似的通知中心)。该方法源码片段如下:

dispatch_async(url_session_manager_processing_queue(), ^{

NSError *serializationError = nil;

responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:[NSData dataWithData:self.mutableData] 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);

}

dispatch_async(dispatch_get_main_queue(), ^{

[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];

});

});

});

整个流程可由下图表示:

所以NSURLSession的实现完全是借鉴了之前的AFNetworking,NSURLSession中维护了两个操作队列,一个用于处理发起请求,等待响应,一个用于处理响应到达后需要执行的回调,对数据进行操作。如果是使用AFHTTPSessionManager的manager方法,初始化session的操作队列的最大并发数为1,则与基于NSURLConnection的AFNetworking完全一样,所有请求和等待响应都在一条线程中执行,然后数据的解析在一个异步并发的操作队列中执行,当然你可以设置该操作队列的最大并发数。

AFNetworking源码分析的更多相关文章

  1. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  2. 【原】AFNetworking源码阅读(五)

    [原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...

  3. 【原】AFNetworking源码阅读(四)

    [原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...

  4. AFNetWorking3.0源码分析

    分析: AFNetWorking(3.0)源码分析(一)——基本框架 AFNetworking源码解析 AFNetworking2.0源码解析<一> end

  5. AFNetworking源码阅读

    get方法: - (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(id)parameters progress:(void ...

  6. AFNetworking源码简析

    AFNetworking基本是苹果开发中网络请求库的标配,它是一个轻量级的网络库,专门针对iOS和OS X的网络应用设计,具有模块化的架构和丰富的APIs接口,功能强大并且使用简单,深受苹果应用开发人 ...

  7. AFNetworking源码品读

    AFNetworking源码品读 AFNetworking这个库几乎是所有苹果开发人员在使用HTTP协议的第一选择,为什么这个库会有这么大的吸引力呢?其实答案就需要问问自己,为什么会用它,而不是别的库 ...

  8. iOS常用框架源码分析

    SDWebImage NSCache 类似可变字典,线程安全,使用可变字典自定义实现缓存时需要考虑加锁和释放锁 在内存不足时NSCache会自动释放存储的对象,不需要手动干预 NSCache的key不 ...

  9. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

随机推荐

  1. ActionBar官方教程(11)自定义ActionBar的样式(含重要的样式属性表及练习示例)

    Styling the Action Bar If you want to implement a visual design that represents your app's brand, th ...

  2. 【HDOJ】3509 Buge's Fibonacci Number Problem

    快速矩阵幂,系数矩阵由多个二项分布组成.第1列是(0,(a+b)^k)第2列是(0,(a+b)^(k-1),0)第3列是(0,(a+b)^(k-2),0,0)以此类推. /* 3509 */ #inc ...

  3. wzplayer2 for windows ActiveX 试用地址

    提供wzplayer2 for windows ActiveX,测试地址:http://www.coolradio.cn/IE.htm 大家使用时候必须允许未签名ActiveX下载和运行,否则将无法正 ...

  4. 如何kill掉TaobaoProtect.exe

    C:\Users\Administrator\AppData\Roaming\TaobaoProtect TaobaoProtect.exe https://technet.microsoft.com ...

  5. bzoj2208

    首先有向图的题目不难想到先tarjan缩点 一个强连通分量中的点的连通数显然是相等: 据说这样直接dfs就可以过了,但显然不够精益求精 万一给定的是一个完全的DAG图怎么办,dfs铁定超时: 首先想, ...

  6. C# DataGridView添加新行的2个方法

    可以静态绑定数据源,这样就自动为DataGridView控件添加 相应的行.假如需要动态为DataGridView控件添加新行,方法有很多种,下面简单介绍如何为DataGridView控件动态添加新行 ...

  7. NuGet -- 如何创建及发布自己的程序包

    STEP 1:在NuGet上注册并获取API Key    首先,你需要在NuGet(https://www.nuget.org/)上注册一个新的账号,然后在My Account页面,获取一个API ...

  8. Azure Backup 简介

    ViswanathTata云 +Enterprise 项目经理 AzureBackup是 Azure恢复服务的一部分,在安全高效的环境中对上传到 Azure的数据提供简单可靠的管理和保护.Azur ...

  9. eight - zoj 1217 poj 1077

    学习了多位大牛的方法,看看到底能把时耗降到多少? A* // zojfulltest: 30000ms # include <stdio.h> # include <ctype.h& ...

  10. Java 回调函数

    下面使用java回调函数来实现一个测试函数运行时间的工具类: 如果我们要测试一个类的方法的执行时间,通常我们会这样做: public class TestObject { /** * 一个用来被测试的 ...