虽然ASIHTTPRequest已经不再更新,大多数人选择了AFNetworking,但是还是有个别公司将ASIHTTPRequest在进行封装一下或者修改一下来满足自己公司的需求,现贴上两者区别
对比 |
ASI | AFN |
更新状态 | 2012年10月份,已经停止更新 | 持续更新中,目前已更新至3.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:
1、进行网络同步请求(sendSynchronousRequest)时,调用该请求接口的操作在哪个线程,同步返回的网络结果就处于哪个线程,因此通常进行网络同步请求时,为了避免阻塞UI主线程,需要在子线程中进行网络请求;
2、进行网络异步请求(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好很多,而这样的应用(或团队)对底层网络控件的定制化要求也非常低。 |
使用:
#import "ViewController.h" #import "JSON.h" #import "ASIFormDataRequest.h" #import "Reachability.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } - (void)createDictionaryIfNotExists { NSString *downloadPath = [NSHomeDirectory()stringByAppendingPathComponent:@"Documents/download"]; if (![[NSFileManager defaultManager]fileExistsAtPath:downloadPath]) { [[NSFileManager defaultManager]createDirectoryAtPath:downloadPath withIntermediateDirectories:YES attributes:nil error:nil]; } NSString *tempPath = [NSHomeDirectory()stringByAppendingPathComponent:@"Documents/temp"]; if (![[NSFileManager defaultManager]fileExistsAtPath:tempPath]) { [[NSFileManager defaultManager]createDirectoryAtPath:tempPath withIntermediateDirectories:YES attributes:nil error:nil]; } } //使用ASI发起GET请求 - (IBAction)getRequestClick:(UIButton *)sender { NSString *urlString = @"http://api.map.baidu.com/telematics/v3/weather?location=郑州&output=json&ak=QZZGSqG9rjS1bQAAfixD9bt7&mcode=abc123"; urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //创建一个ASI的请求类,ASIHTTPRequest用于GET请求 ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:urlString]]; //设置ASI请求的代理,请求结束后,ASI就会调用相应的代理方法。 request.delegate = self; //设置请求结束的回调方法(须先实现协议),如果不设定,那么ASI默认调用协议中得到的结束方法;如果设定,那么就不再调用协议方法,只调用设置的方法 request.didFinishSelector = @selector(getRequsetFinish:); //设置请求的tag值 //request.tag = 1; //同一个界面的多种请求,在请求完成时(1)可以通过tag值区分哪个请求完成;(2)也可以发送请求时绑定不同的请求结束调用方法。 //startAsynchronous 异步请求(请求数据过程中不会阻塞主线程) ; startSynchronous 同步请求(请求过程会阻止主线程,如果网速慢或者请求的数据量比较大,就会导致主线程长时间处于卡死状态) [request startAsynchronous]; //显示状态栏上的网络请求标志,YES表示圈一直在转 //[[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES]; } //发送POST请求 - (IBAction)postRequest:(UIButton *)sender { //ASIFormDataRequest 发送POST请求 ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]; //setPostValue: forKey: 添加POST参数 [request setPostValue:@"ming" forKey:@"name"]; [request setPostValue:@"20" forKey:@"age"]; request.delegate = self; request.didFinishSelector = @selector(postRequestFinish:); [request startAsynchronous]; } //下载文件 - (IBAction)downloadFileClick:(UIButton *)sender { //如果正在下载,不再发送下载请求 if (_downloadRequest) { return; } NSURL *url = [NSURL URLWithString:@"http://localhost:8080/FileUploadServer/net.zip"]; _downloadRequest = [ASIHTTPRequest requestWithURL:url]; //设置下载路径,设置过下载路经之后,ASI就会把这次请求当作下载请求,自动把收到的数据存入路径文件,request.responseData中就不会在自动拼数据。设置的路径文件夹必须保证存在(不会自动创建)。 [self createDictionaryIfNotExists]; NSString *path = [NSHomeDirectory()stringByAppendingPathComponent:@"Documents/download/a.zip"]; _downloadRequest.downloadDestinationPath = path; _downloadRequest.delegate = self; //设置下载进度代理,每当下载到新数据时,ASI就会调用代理对象的协议方法 _downloadRequest.downloadProgressDelegate = self; _downloadRequest.didFinishSelector = @selector(downloadRequestFinish:); //实现断点下载 //设置文件的临时下载路径 (必须保证存在) temporary,临时的 _downloadRequest.temporaryFileDownloadPath = [NSHomeDirectory()stringByAppendingPathComponent:@"Documents/temp/a.zip"]; //设置是否可以断点续传 Resume ,继续 _downloadRequest.allowResumeForFileDownloads = YES; [_downloadRequest startAsynchronous]; } //暂停下载 - (IBAction)stopDownload:(UIButton *)sender { [_downloadRequest cancel]; _downloadRequest = nil; } //上传文件 - (IBAction)uploadFileClick:(UIButton *)sender { ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://localhost:8080/FileUploadServer/UploadServlet"]]; //如果上传小文件,setPostBody 直接把文件放入请求体即可 //[request setPostBody:data]; //把一个文件的数据设置到请求体中(上传大文件,直接存入硬盘) NSString *path = [[NSBundle mainBundle]pathForResource:@"file" ofType:@"zip"]; [request setPostBodyFilePath:path]; //setShouldStreamPostDataFromDisk 设置是否使用输入流从磁盘中读取数据到请求体,对于大文件必须使用输入流读入。 [request setShouldStreamPostDataFromDisk:YES]; //设置上传进度代理 request.uploadProgressDelegate = self; request.delegate = self; request.didFinishSelector = @selector(uploadRequestFinish:); [request startAsynchronous]; } //判断当前网络状态 - (IBAction)isNetWorkClick:(UIButton *)sender { //Reachability 能够判断当前的网络状态 Reachability *reach = [Reachability reachabilityForInternetConnection]; switch (reach.currentReachabilityStatus) { case kNotReachable: _netWorkLabel.text = @"没有网络"; break; case kReachableViaWWAN: _netWorkLabel.text = @"手机网络"; break; case kReachableViaWiFi: _netWorkLabel.text = @"WIFI网络"; default: break; } //网络状态发生变化时的通知 //extern NSString *const kReachabilityChangedNotification } //ASI请求失败时调用 - (void)requestFailed:(ASIHTTPRequest *)request { NSLog(@"ASI请求失败原因:%@",request.error); } //ASI请求成功时调用 - (void)requestFinished:(ASIHTTPRequest *)request { //request.responseData 请求得到的数据 //request.responseString 请求数据按照UTF8转换的字符串 //NSLog(@"%@",request.responseString); //使用 SBJSON 解析数据 NSDictionary *dic = request.responseString.JSONValue; NSLog(@"%@",dic); } //请求数据时调用,如果实现了这个方法,ASI就不再自动拼接数据 //- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data //{ // NSLog(@"收到数据了"); //} - (void)getRequsetFinish:(ASIHTTPRequest *)request { NSLog(@"自己设定的协议方法\n%@",request.responseString); } - (void)postRequestFinish:(ASIFormDataRequest *)request { NSLog(@"%@",request.responseString); } - (void)downloadRequestFinish:(ASIHTTPRequest *)request { NSLog(@"下载完成"); _downloadRequest = nil; } //每当进度更新时调用 - (void)setProgress:(float)newProgress { _progressVIew.progress = newProgress; } - (void)uploadRequestFinish:(ASIFormDataRequest *)request { NSLog(@"上传完成"); }