点击查看美团、百度、蚂蚁技术专家的软件质量保障技术实践>> 了解详情
写点什么

对比 iOS 网络组件:AFNetworking VS ASIHTTPRequest

  • 2013-02-28
  • 本文字数:4217 字

    阅读完需:约 14 分钟

在开发 iOS 应用过程中,如何高效的与服务端 API 进行数据交换,是一个常见问题。一般开发者都会选择一个第三方的网络组件作为服务,以提高开发效率和稳定性。这些组件把复杂的网络底层操作封装成友好的类和方法,并且加入异常处理等。

那么,大家最常用的组件是什么?这些组件是如何提升开发效率和稳定性的?哪一款组件适合自己,是 AFNetworking(AFN)还是 ASIHTTPRequest(ASI)?几乎每一个 iOS 互联网应用开发者都会面对这样的选择题,要从这两个最常用的组件里选出一个好的还真不是那么容易。

单单从两个控件版本提交的时间节点来看,AFN 的第一个提交是 2011 年的 1 月 1 日,那个时候 ASI 早已是 1.8+ 的版本了;而当 AFN 发布 1.0 版,2012 年 10 月份的时候,ASI 早早的已经停止更新了。这样看起来,AFN 是 ASI 的继任者,似乎不存在之前提到的选择困难的问题,而事实并非如此。本文将从用法、功能、性能和原理几个方面对二者进行简单对比,看看二者之间到底存在着怎样的区别,到底应该如何选择。

1、用法对比

首先,从推荐用法上就可以看出二者设计理念上大有不同。

图1,AFN 的示例代码,发起请求(出自: Posts.m

AFN 官方推荐的使用方法是,为一系列相关的请求定义一个 HTTPClient ,共用一个 BaseURL。每次请求把 URL 中除 BaseURL 的 Path 部分做为参数传给 HTTPClient 的静态方法,并注册一个 Block 用于回调。

图2,ASI 示例代码,发起异步请求(出自: ASIHTTPRequestTests.m

ASI 推荐使用方法就非常传统,每一个请求都由构造方法初始化一个(共享)实例,通过这个实例配置参数并发起请求。ASI 最初使用 delegate 模式回调,在 iOS SDK 支持 Block 之后也提供了注册 Block 的实例方法。

以上引用的两段代码都出自各自项目的示例工程。对比两段代码可以很清楚的看出,同样是发起一个最普通的异步请求,使用 AFN 只需要调用一个静态方法,但代码可读性较差;而 ASI 的示例看起来更清晰,但需要调用多个实例方法才能完成一次请求。AFN 的设计更加工程化,或者说对使用者更友好,而 ASI 的设计更经典,典型的 OOP。

除了初级用法上的区别,二者的高级功能和对扩展的支持也颇有不同。

2、高级功能

AFN 只封装了一些常用功能,满足基本需求,而直接忽略了很多扩展功能。例如:AFN 默认没有封装同步请求,如果开发者需要使用同步请求,则需要重写 getPath:parameters:success:failure 方法,对 AFHTTPRequestOperation 进行同步处理;而 ASI 则是直接通过调用一个 startSynchronous 方法。

此外 AFN 针对 JSON XML PList Image 四种数据结构封装了各自处理器,开发者可以把处理器注册到操作队列中,直接在回调方法中获得格式化以后的数据。在示例工程中就使用了 JSON 处理器:把 AFJSONRequestOperation 注册到操作队列里。

图3,AFN 示例代码,初始化自定义的HTTPClient(出自: AFAppDotNetAPIClient.m

而 ASI 在这方面显得更原始,没有针对任何数据类型做特别封装,只是预留了各种接口和工具供开发者自行扩展。ASI 比 AFN 提供更多扩展功能还有一个原因,它把许多内部用到的功能也抽象成类和方法。例如:

ASIHTTPRequestDataCompressor 和 ASIHTTPRequestDataDecompressor 两个类,只用于压缩本地文件,构造 POST Body 和解压缩返回数据,但这两个类仍然被设计为独立功能,提供了对多种数据结构进行压缩和解压缩的方法。

对比二者的高级功能和对扩展的支持后,可以看出 AFN 把初级功能(或者叫常用功能)做到了 90 分。调用方式够简单,处理器够丰富,使用者用起来可以算是轻松加愉快。但它放弃了对高级功能的支持,要满足较复杂的需求,就要大费周折了,在这方面最多只有 40 分。而 ASI 显然不满足于做好初级功能,但为了提供更丰富的可扩展接口,导致初级功能用起来也要花上一些力气。虽然 ASI 单独提供了支持 Amazon S3 Rackspace Cloud Files 的控件,但对于生在红旗下的我朝开发者来说基本没用,所以在初级功能的支持上 ASI 能得个 70 分,牺牲了初级功能的易用性,换来的是良好的扩展性,在高级功能的使用上远远好于 AFN,也能得个 70 分。

从使用角度对比过后,基本上对这两个项目有一个整体上的认识,再深入下去看看二者的性能如何。

3、性能对比

我分别用 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 进一步讨论。

4、原理分析

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。这一点也是造成二者性能差别的原因之一。

总结

通过以上的对比,基本可以这样评价:AFN 适合逻辑简单的应用,或者更适合开发资源尚不丰富的团队,因为 AFN 的易用性要比 ASI 好很多,而这样的应用(或团队)对底层网络控件的定制化要求也非常低。ASI 更适合已经发展了一段时间的应用,或者开发资源相对丰富的团队,因为往往这些团队(或他们的应用)已经积累了一定的经验,无论是产品上还是技术上的。需求复杂度就是在这种时候高起来,而且底层订制的需求也越来越多,此时 AFN 就很难满足需求,需要牺牲一定的易用性,使用 ASI 作为网络底层控件。SegmentFault 开源客户端现在被设计为一款简单的阅读客户端,几乎没有定制要求,因此,目前我选择了 AFN 作为网络控件。

以上对 ASI 和 AFN 两款最常用的 iOS 底层网络控件做了初步的介绍,要更深入的了解两款控件,还需要大家继续研究各自的源码。大家遇到任何关于 iOS 的技术问题都可以在 http://segmentfault.com/t/ios 进行讨论。另外大家也可以持续关注 SegmentFault 的开源客户端,与更多的开发者共同探讨 iOS 开发技术。

作者简介

高嘉峻(微博: @gaosboy ),SegmentFault.com 联合创始人,杭州 iOS 开发者沙龙发起人,资深 iOS 开发者。

2013-02-28 00:0146035

评论

发布
暂无评论
发现更多内容

产品经理:「点这里,我要跳到任何我想跳的页面」—— 解耦提效神器「统跳路由」

百瓶技术

ios 前端 客户端 路由

Milvus 图形化管理工具 Attu 来袭!

Zilliz

数据库

了解 DevOps,必读这十本书!

禅道项目管理

DevOps

「前端CI/CD系列」第二篇:如何用建木CI更新七牛云CDN证书

Jianmu

CDN 自动化运维 七牛云 建木CI

Guitar Pro教程之如何设置MIDI键盘

懒得勤快

一个关于 += 的谜题

AlwaysBeta

Python 编程语言

掌握这些招数,你也能写出HR眼中的高分简历

Tom弹架构

求职面试

大咖说|对话路特斯科技副总裁李博:如何看待智能驾驶的未来?

大咖说

阿里巴巴 智能 汽车 无人驾驶 路特斯

虎符交易所2022年首届交易大赛 最高瓜分5万USDT奖励

区块链前沿News

虎符交易所

杭州AI开发者Meetup报名开启!

百度大脑

如何通过 draftjs 设计留言框

全象云低代码

前端 低代码 留言 draftjs 留言框

乘冬奥之风:北京2022年冬奥会用户信息获取偏好专题分析

易观分析

冬奥会用户分析

web前端培训:vue3源码中细节知多少

@零度

Vue 前端开发

优化| 手把手教你学会杉数求解器(COPT)的安装、配置与测试

杉数科技

线性规划 求解器 优化求解器 混合整数规划 杉数科技

“pip不是内部或外部命令,也不是可运行的程序或批处理文件” 到底有多么神秘

华为云开发者联盟

Python pip 批处理 scripts pip install

Go 语言入门很简单:读写锁

宇宙之一粟

读写锁 Go 语言 2月月更

前所未有的 Milvus 源码架构解析

Zilliz

【重磅发布】蚂蚁动态卡片,让 App 首页实现敏捷更新

蚂蚁集团移动开发平台 mPaaS

ios android 前端 mPaaS

Deep dive #2:API 与 Python SDKs 详解

Zilliz

Python 数据库

百度飞桨大企业开放创新中心联合赋能计划启动!助力浦东产业智能化升级

百度大脑

为什么我的 ORDER BY create_time ASC 变成了 order by ASC

LigaAI

Java 数据库 sql 程序员

Android技术分享| 【你画我猜】Android 快速实现

anyRTC开发者

音视频 移动开发 互动白板 Andriod 你画我猜

ModStartCMS模块化建站系统 v3.3.0 组件功能升级,事件触发增强

ModStart开源

Hudi Bucket Index 在字节跳动的设计与实践

字节跳动数据平台

数据库 字节跳动 数据湖 Hudi

oracle数据库审计用什么数据库审计软件好?可以用什么方式部署?

行云管家

数据库 IT运维 数据库审计

混合云管平台哪家强?采购时候需要注意什么?

行云管家

混合云 云管平台

极致用云,数智护航

阿里云云效

阿里云 DevOps 运维 云原生 运维安全

边缘计算场景下Service Mesh的延伸和扩展

华为云原生团队

开源 边缘计算 边缘技术 边缘 边缘云

移动开发er,10万奖金等你来战!

Speedoooo

活动 前端开发 移动开发 黑客马拉松 黑客松

云效发布策略指南|滚动、分批、灰度怎么选?

阿里云云效

云计算 阿里云 云原生 持续交付 发布策略

火山引擎举办视频云科技原力峰会,发布面向体验的全新视频云产品矩阵

字节跳动视频云技术团队

音视频

对比iOS网络组件:AFNetworking VS ASIHTTPRequest_iOS_高嘉峻_InfoQ精选文章