在开发iOS应用过程当中,如何高效的与服务端API进行数据交换,是一个常见问题。通常开发者都会选择一个第三方的网络组件做为服务,以提升开发效率和稳定性。这些组件把复杂的网络底层操做封装成友好的类和方法,而且加入异常处理等。html
那么,你们最经常使用的组件是什么?这些组件是如何提高开发效率和稳定性的?哪一款组件适合本身,是 AFNetworking(AFN)仍是 ASIHTTPRequest(ASI)?几乎每个iOS互联网应用开发者都会面对这样的选择题,要从这两个最经常使用的组件里选出一个好的还真不是那么容易。ios
单单从两个控件版本提交的时间节点来看,AFN的第一个提交是2011年的1月1日,那个时候ASI早已经是1.8+的版本了;而当AFN发布1.0版,2012年10月份的时候,ASI早早的已经中止更新了。这样看起来,AFN是ASI的继任者,彷佛不存在以前提到的选择困难的问题,而事实并不是如此。本文将从用法、功能、性能和原理几个方面对两者进行简单对比,看看两者之间到底存在着怎样的区别,到底应该如何选择。git
首先,从推荐用法上就能够看出两者设计理念上大有不一样:github
AFN官方推荐的使用方法是,为一系列相关的请求定义一个HTTPClient,共用一个BaseURL。每次请求把URL中除BaseURL的Path部分作为参数传给HTTPClient的静态方法,并注册一个Block用于回调。
ASI推荐使用方法就很是传统,每个请求都由构造方法初始化一个(共享)实例,经过这个实例配置参数并发起请求。ASI最初使用delegate模式回调,在iOS SDK支持Block以后也提供了注册Block的实例方法。
以上引用的两段代码都出自各自项目的示例工程。对比两段代码能够很清楚的看出,一样是发起一个最普通的异步请求,使用AFN只须要调用一个静态方法,但代码可读性较差;而ASI的示例看起来更清晰,但须要调用多个实例方法才能完成一次请求。AFN的设计更加工程化,或者说对使用者更友好,而ASI的设计更经典,典型的OOP。
除了初级用法上的区别,两者的高级功能和对扩展的支持也很有不一样。
二、高级功能
AFN只封装了一些经常使用功能,知足基本需求,而直接忽略了不少扩展功能。例如:AFN默认没有封装同步请求,若是开发者须要使用同步请求,则须要重写getPath:parameters:success:failure方法,对AFHTTPRequestOperation进行同步处理;而ASI则是直接经过调用一个startSynchronous方法。
此外AFN针对JSON、XML、PList和Image四种数据结构封装了各自处理器,开发者能够把处理器注册到操做队列中,直接在回调方法中得到格式化之后的数据。在示例工程中就使用了JSON处理器:把AFJSONRequestOperation注册到操做队列里。
而ASI在这方面显得更原始,没有针对任何数据类型作特别封装,只是预留了各类接口和工具供开发者自行扩展。ASI比AFN提供更多扩展功能还有一个缘由,它把许多内部用到的功能也抽象成类和方法。例如:
ASIHTTPRequestDataCompressor和ASIHTTPRequestDataDecompressor两个类,只用于压缩本地文件,构造POST Body和解压缩返回数据,但这两个类仍然被设计为独立功能,提供了对多种数据结构进行压缩和解压缩的方法。
对比两者的高级功能和对扩展的支持后,能够看出AFN把初级功能(或者叫经常使用功能)作到了90分。调用方式够简单,处理器够丰富,使用者用起来能够算是轻松加愉快。但它放弃了对高级功能的支持,要知足较复杂的需求,就要大费周折了,在这方面最多只有40分。而ASI显然不知足于作好初级功能,但为了提供更丰富的可扩展接口,致使初级功能用起来也要花上一些力气。虽然ASI单独提供了支持Amazon S3和Rackspace Cloud Files的控件,但对于生在红旗下的我朝开发者来讲基本没用,因此在初级功能的支持上ASI能得个70分,牺牲了初级功能的易用性,换来的是良好的扩展性,在高级功能的使用上远远好于AFN,也能得个70分。
从使用角度对比事后,基本上对这两个项目有一个总体上的认识,再深刻下去看看两者的性能如何。
三、性能对比
我分别用AFN和ASI进行了测试,测试环境以下:iPhone5,联通3G信号全满,室内静止状态,请求国内双线机房独立服务器的静态文件,1~20K共20个文件,每一个文件请求20次,记录从建立请求到彻底下载文件的耗时,结果以下:
图4,AFN连续访问1 ~ 20K文件耗时
图5,ASI连续访问1 ~ 20K文件耗时
图4是AFN的记录图,绿色为20次请求中耗时最久的一次,蓝色为耗时最短的一次,黄色为去除最大值和最小值的18次平均值。从这个图能够看出,AFN最开始建立对象耗时近2.5秒,随后稳定下来,在3K、7K、15K和20K时出现了抖动。图5是ASI作相同测试的结果,首次建立对象近2.25秒,略优于AFN,一样在5K、11K、13K、14K和16K发生了一些抖动,但抖动幅度彷佛小于AFN,可见稳定性更好一些。
下边是把两者的测试结果放在一块儿的对比图,能够更直观的比较两者的区别。
图6,ASI和AFN耗时最大值对比
图6的最大值对比能够更明显的看出两者的抖动对比,ASI略好一些。
图7,ASI和AFN耗时最小值对比
图7的最小值对比能够看出,在每个大小的测试中ASI的最佳性能彷佛都要优于AFN。
图8,ASI和AFN耗时平均值对比
图8是耗时平均值的对比,更可以说明问题。文件小于12K的测试中ASI的性能优点并无很是明显,超过12K之后,ASI优点开始明显起来,每一次请求都要比AFN节约20% ~ 30%,近0.1秒。同时从这张图上还能够看出,随着下载文件变大,请求耗时并非线形增加的,这是因为一次请求大部分时间都消耗在创建链接上,而真正接收数据只占用了极少时间,这个问题不在本篇文章的讨论范围,因此很少说,有兴趣的读者能够移步http://segmentfault.com/t/ios进一步讨论。
四、原理分析
ASI的性能彷佛全面优于AFN,那下边从两者的实现原理上看一下究竟是什么缘由形成这种差距。ASI基于CFNetwork框架开发,而AFN基于NSURL,底层的区别是致使两者性能差距的重要缘由之一。
图9,ASI和AFN以及底层框架的关系
咱们知道全部网络通讯的基础是Socket,一个Socket与另外一个链接并传送数据。BSD Socket是一类最多见的Socket抽象接口。
Core Foundation框架中的CFSocket就是基于BSD Socket开发的。它几乎涵盖了BSD Socket的所有功能,更重要的是把Socket整合到事件的处理循环中。Core Founda-tion中较高层的CFStream是基于CFSocket开发的读写流支持。
CFNetwork是基于Core Foundation中CFStream的一个底层高性能网络框架,它由提供基础服务的CFSocketStream,支持HTTP协议的CFHTTP,基于CFHTTP用于身份认证的CFHTTPAuthentication和支持FTP协议的CFFTP组成。
正如图9所示,ASI是基于CFHTTP开发的一个组件;而AFN的基础——NSURL,也是基于CFNetwork开发的。也就是说ASI相比AFN更加底层,这就从必定程度上形成两者的性能差距。
另外一个方面,虽然两者都使用NSOperation和NSOperationQueue实现但底层的区别也致使实现方式上有很是大的差异。
ASI的直接操做对象ASIHTTPRequest是NSOperation的子类,实现了NSCopying协议。在initialize和initWithURL:方法中初始化相关属性并配置一系列请求相关参数默认值。此外,ASIHTTPRequest还提供了一系列的实例方法用来配置请求对象。在异步请求的处理上,ASIHTTPRequest对象初始化结束后,在startAsynchronous方法中把对象加入共享操做队列。此后,包括建立CFHTTPMessageRef,也就是处理网络请求的主要对象(事实上是一个指向__CFHTTPMessage结构的指针),在内的全部操做都在ASIHTTPRequest对象所属的子线程中完成。
AFN的直接操做对象AFHTTPClient不一样于ASI,是一个实现了NSCoding和NSCopying协议的NSObject子类。AFHTTPClient是一个封装了一系列操做方法的“工具类”,处理请求的操做类是一系列单独的,基于NSOperation封装的,AFURLConnectionOperation的子类。AFN的示例代码中经过一个静态方法,使用dispatch_once()的方式建立AFHTTPClient的共享实例,这也是官方建议的使用方法。在建立AFHTTPClient的初始化方法中,建立了OperationQueue并设置一系列参数默认值。在getPath:parameters:success:failure方法中建立NSURLRequest,以NSURLRequest对象实例做为参数,建立一个NSOperation,并加入在初始化发方中建立的NSOperationQueue。以上操做都是在主线程中完成的。在NSOperation的start方法中,以此前建立的NSURLRequest对象为参数建立NSURLConnection并开启连结。
在异步回调的处理上两者也有区别,ASI采起的是CFHTTP请求完成,直接回调ASIHTTPRequest的实例方法,经过储存的实例对象记录的信息完成Delegate模式或Block模式的回调。而AFN则直接使用了NSOperation的completionBlock属性。
这些实现方式也能够看出,ASI显得更加底层,并无过多使用Cocoa框架中已经封装的API,而AFN则更加实用主义,逻辑简单清晰,大量使用了框架API。这一点也是形成两者性能差异的缘由之一。segmentfault
对比 |
ASI | AFN |
更新状态 | 2012年10月份,已经中止更新 | 持续更新中,目前已更新至2.0版 |
介绍 |
ASI的直接操做对象ASIHTTPRequest,是一个实现了了NSCopying协议的NSOperation子类。
在initialize和initWithURL:方法中初始化相关属性并配置一系列请求相关参数默认值。此外,ASIHTTPRequest还提供了一系列的实例方法用来配置请求对象。
|
AFN的直接操做对象AFHTTPClient,是一个实现了NSCoding和NSCopying协议的NSObject子类。AFHTTPClient是一个封装了一系列操做方法的“工具类”,处理请求的操做类是一系列单独的,基于NSOperation封装的,AFURLConnectionOperation的子类。 |
线程处理模式 |
每个请求都由构造方法初始化一个(共享)实例,经过这个实例配置参数并发起请求。ASI最初使用delegate模式回调,在iOS SDK支持Block以后也提供了注册Block的实例方法。
ASI采起的是CFHTTP请求完成,直接回调ASIHTTPRequest的实例方法,经过储存的实例对象记录的信息完成Delegate模式或Block模式的回调。
在异步请求的处理上,ASIHTTPRequest对象初始化结束后,在startAsynchronous方法中把对象加入共享操做队列。此后,包括建立CFHTTPMessageRef,也就是处理网络请求的主要对象(事实上是一个指向__CFHTTPMessage结构的指针),在内的全部操做都在ASIHTTPRequest对象所属的子线程中完成。
|
AFN的示例代码中经过一个静态方法,使用dispatch_once()的方式建立AFHTTPClient的共享实例,这也是官方建议的使用方法。在建立AFHTTPClient的初始化方法中,建立了OperationQueue并设置一系列参数默认值。在getPath:parameters:success:failure方法中建立NSURLRequest,以NSURLRequest对象实例做为参数,建立一个NSOperation,并加入在初始化发方中建立的NSOperationQueue。
以上操做都是在主线程中完成的。在NSOperation的start方法中,以此前建立的NSURLRequest对象为参数建立NSURLConnection并开启连结。
|
数据处理模式 | ASI在这方面显得更原始,没有针对任何数据类型作特别封装,只是预留了各类接口和工具供开发者自行扩展。 | AFN针对JSON、XML、PList和Image四种数据结构封装了各自处理器,开发者能够把处理器注册到操做队列中,直接在回调方法中得到格式化之后的数据。 |
同步请求 |
ASI则是直接经过调用一个startSynchronous方法。
|
AFN默认没有封装同步请求,若是开发者须要使用同步请求,则须要重写getPath:parameters:success:failure方法,对AFHTTPRequestOperation进行同步处理 |
异步回调的处理 | 【使用AFNetworking进行网络异步请求时,block:(void(^)代码块实际返回到UI主线程中。即便在子线程中使用AFNetWorking进行网络的异步请求,block:(void(^)代码块仍然返回到UI主线程中(AF框架,它里面已经create了异步线程 )。所以不管当前处在主线程仍是子线程,异步返回均返回到UI主线程中。】 |
为一系列相关的请求定义一个HTTPClient,共用一个BaseURL。每次请求把URL中除BaseURL的Path部分作为参数传给HTTPClient的静态方法,并注册一个Block用于回调。
AFN则直接使用了NSOperation的completionBlock属性。
|
基于的底层开发框架
|
CFNetwork框架
使用CFnetwork而不是Cocoa框架NSURL有几点好处。CFNetwork更加专一于网络协议,而NSURL更加专一于数据访问,好比经过HTTP或者FTP传输数据。尽管NSURL的确也提供了一些可配置功能,但是CFNetwork提供的要多的多。另外NSURL还须要你使用Objective_c。若是作不到这点的话,仍是应该使用CFNetwork
|
NSURL
【使用iOS5.0 SDK NSURLConnection:
一、进行网络同步请求(sendSynchronousRequest)时,调用该请求接口的操做在哪一个线程,同步返回的网络结果就处于哪一个线程,所以一般进行网络同步请求时,为了不阻塞UI主线程,须要在子线程中进行网络请求;
二、进行网络异步请求(sendAsynchronousRequest)时,block:(void(^)代码块实际返回到子线程中。所以,此时如须要向UI线程发送通知,则须要跳转到主线程中发送通知dispatch_async(dispatch_get_main_queue(), ^{});】
|
底层开发矿建介绍 |
CFNetwork是基于Core Foundation中CFStream的一个底层高性能网络框架,它由提供基础服务的CFSocketStream,支持HTTP协议的CFHTTP,基于CFHTTP用于身份认证的CFHTTPAuthentication和支持FTP协议的CFFTP组成。
Core Foundation框架中的CFSocket就是基于BSD Socket开发的。它几乎涵盖了BSD Socket的所有功能,更重要的是把Socket整合到事件的处理循环中。Core Founda-tion中较高层的CFStream是基于CFSocket开发的读写流支持。
|
如图所示,ASI是基于CFHTTP开发的一个组件;而AFN的基础——NSURL,也是基于CFNetwork开发的,也就是说ASI相比AFN更加底层。 |
性能对比 | AFN请求优于ASI | |
总结 | ASI更适合已经发展了一段时间的应用,或者开发资源相对丰富的团队,由于每每这些团队(或他们的应用)已经积累了必定的经验,不管是产品上仍是技术上的。需求复杂度就是在这种时候高起来,并且底层订制的需求也愈来愈多,此时AFN就很难知足需求,须要牺牲必定的易用性,使用ASI做为网络底层控件。 | AFN适合逻辑简单的应用,或者更适合开发资源尚不丰富的团队,由于AFN的易用性要比ASI好不少,而这样的应用(或团队)对底层网络控件的定制化要求也很是低。 |
总结
经过以上的对比,基本能够这样评价:AFN适合逻辑简单的应用,或者更适合开发资源尚不丰富的团队,由于AFN的易用性要比ASI好不少,而这样的应用(或团队)对底层网络控件的定制化要求也很是低。ASI更适合已经发展了一段时间的应用,或者开发资源相对丰富的团队,由于每每这些团队(或他们的应用)已经积累了必定的经验,不管是产品上仍是技术上的。需求复杂度就是在这种时候高起来,并且底层订制的需求也愈来愈多,此时AFN就很难知足需求,须要牺牲必定的易用性,使用ASI做为网络底层控件。SegmentFault开源客户端如今被设计为一款简单的阅读客户端,几乎没有定制要求,所以,目前我选择了AFN做为网络控件。
以上对ASI和AFN两款最经常使用的iOS底层网络控件作了初步的介绍,要更深刻的了解两款控件,还须要你们继续研究各自的源码。你们遇到任何关于iOS的技术问题均可以在
http://segmentfault.com/t/ios
进行讨论。另外你们也能够持续关注SegmentFault的开源客户端
https://github.com/gaosboy/iOSSF
,与更多的开发者共同探讨iOS开发技术。服务器