本文为大家介绍了iOS开发中ASIHttpRequest如何创建和执行request,其中包括同步请求,异步请求,使用Block,使用队列,取消异步请求等等内容。
创建NSOperationQueue,这个Cocoa架构的执行任务(NSOperation)的任务队列。我们通过ASIHTTPRequest.h的源码可以看到,此类本身就是一个NSOperation的子类。也就是说它可以直接被放到任务队列中并被执行。
同步请求
同步请求会在当前线程中执行,使用error属性来检查结束状态(要下载大文件,则需要设定downloadDestinationPath来保存文件到本地):
- - (IBAction)grabURL:(id)sender
- {
- NSURL *url = [NSURL URLWithString:@"http://www.dreamingwish.com"];
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request startSynchronous];
- NSError *error = [request error];
- if (!error) {
- NSString *response = [request responseString];
- }
- }
同步请求会阻塞主线程的执行,这导致用户界面不响应用户操作,任何动画都会停止渲染。
异步请求
下面是最简单的异步请求方法,这个request会在全局的NSOperationQueue中执行,若要进行更复杂的操作,我们需要自己创建NSOperationQueue或者ASINetworkQueue,后面会讲到。
- - (IBAction)grabURLInBackground:(id)sender
- {
- NSURL *url = [NSURL URLWithString:@"http://www.dreamingwish.com"];
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setDelegate:self];
- [request startAsynchronous];
- }
- - (void)requestFinished:(ASIHTTPRequest *)request
- {
- // Use when fetching text data
- NSString *responseString = [request responseString];
- // Use when fetching binary data
- NSData *responseData = [request responseData];
- }
- - (void)requestFailed:(ASIHTTPRequest *)request
- {
- NSError *error = [request error];
- }
使用block
在平台支持情况下,ASIHTTPRequest1.8以上支持block。
- - (IBAction)grabURLInBackground:(id)sender
- {
- NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
- __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setCompletionBlock:^{
- // Use when fetching text data
- NSString *responseString = [request responseString];
- // Use when fetching binary data
- NSData *responseData = [request responseData];
- }];
- [request setFailedBlock:^{
- NSError *error = [request error];
- }];
- [request startAsynchronous];
- }
注意,声明request时要使用__block修饰符,这是为了告诉block不要retain request,以免出现retain循环,因为request是会retain block的。
使用队列
创建NSOperationQueue或者ASINetworkQueue队列,我们还可以设定最大并发连接数:maxConcurrentOperationCount
- - (IBAction)grabURLInTheBackground:(id)sender
- {
- if (![self queue]) {
- [self setQueue:[[[NSOperationQueue alloc] init] autorelease]];
- [self queue].maxConcurrentOperationCount = 4;
- }
- NSURL *url = [NSURL URLWithString:@"http://www.dreamingwish.com"];
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setDelegate:self];
- [request setDidFinishSelector:@selector(requestDone:)];
- [request setDidFailSelector:@selector(requestWentWrong:)];
- [[self queue] addOperation:request]; //queue is an NSOperationQueue
- }
- - (void)requestDone:(ASIHTTPRequest *)request
- {
- NSString *response = [request responseString];
- }
- - (void)requestWentWrong:(ASIHTTPRequest *)request
- {
- NSError *error = [request error];
- }
如果不设定selector,那么系统会使用默认的requestFinished: 和 requestFailed:方法
如果需要对队列里面的每个request进行区分,那么可以设定request的userInfo属性,它是个NSDictionary,或者更简单的方法是设定每个request的tag属性,这两个属性都不会被发送到服务器。
不要使用request的URL来区分每个request,因为URL可能会改变(例如重定向),如果需要使用request的URL,使用[request originalURL],这个将永远返回第一个url。
对于ASINetworkQueue
ASINetworkQueue是NSOperationQueue的子类,提供更高级的特性(ASINetworkQueue的代理函数):
- requestDidStartSelector
当一个request开始执行时,这个代理函数会被调用。 - requestDidReceiveResponseHeadersSelector
当队列中的request收到服务器返回的头信息时,这个代理函数会被调用。对于下载很大的文件,这个通常比整个request的完成要早。 - requestDidFinishSelector
当每个request完成时,这个代理函数会被调用。 - requestDidFailSelector
当每个request失败时,这个代理函数会被调用。 - queueDidFinishSelector
当队列完成(无论request失败还是成功)时,这个代理函数会被调用。
ASINetworkQueues与NSOperationQueues稍有不同,加入队列的request不会立即开始执行。如果队列打开了进度开关,那么队列开始时,会先对所有GET型request进行一次HEAD请求,获得总下载大小,然后真正的request才被执行。
向一个已经开始进行的ASINetworkQueue 加入request会怎样?
如果你使用ASINetworkQueue来跟踪若干request的进度,只有当新的request开始执行时,总进度才会进行自适应调整(向后移动)。ASINetworkQueue不会为队列开始后才加入的request进行HEAD请求,所以如果你一次向一个正在执行的队列加入很多request,那么总进度不会立即被更新。
如果队列已经开始了,不需要再次调用[queue go]。
当ASINetworkQueue中的一个request失败时,默认情况下,ASINetworkQueue会取消所有其他的request。要禁用这个特性,设置 [queue setShouldCancelAllRequestsOnFailure:NO]。
ASINetworkQueues只可以执行ASIHTTPRequest操作,二不可以用于通用操作。试图加入一个不是ASIHTTPRequest的NSOperation将会导致抛出错误。
取消异步请求
取消一个异步请求(无论request是由[request startAsynchronous]开始的还是从你创建的队列中开始的),使用[request cancel]即可。注意同步请求不可以被取消。
注意,如果你取消了一个request,那么这个request将会被视为请求失败,并且request的代理或者队列的代理的失败代理函数将被调用。如果你不想让代理函数被调用,那么将delegate设置为nil,或者使用clearDelegatesAndCancel方法来取消request。
clearDelegatesAndCancel 将会首先清除所有的代理和block。
当使用ASINetworkQueue时,如果取消了队列中的一个request,那么队列中其他所有request都会被取消,可以设置shouldCancelAllRequestsOnFailure的值为NO来避免这个现象。
安全地控制delegate防止request完成之前代理被释放
request并不retain它们的代理,所以有可能你已经释放了代理,而之后request完成了,这将会引起崩溃。大多数情况下,如果你的代理即将被释放,你一定也希望取消所有request,因为你已经不再关心它们的返回情况了。如此做:
- // 代理类的dealloc函数
- - (void)dealloc
- {
- [request clearDelegatesAndCancel];
- [request release];
- ...
- [super dealloc];
- }
iOS开发ASIHTTPRequest断点续传
本文为大家介绍了iOS开发ASIHTTPRequest断点续传(下载)的内容,其中包括ASIHTTPRequest可以恢复中断的下载,设置一个临时下载路径,断点续传的工作原理等等内容。
从0.94版本开始,ASIHTTPRequest可以恢复中断的下载。
这个特性只对下载数据到文件中有效,你必须为一下情况的request设置allowResumeForFileDownloads 为YES:
- 任何你希望将来可以断点续传的下载(否则,ASIHTTPRequest会在取消或者释放内存时将临时文件删除)
- 任何你要进行断点续传的下载
另外,你必须自己设置一个临时下载路径(setTemporaryFileDownloadPath),这个路径是未完成的数据的路径。新的数据将会被添加到这个文件,当下载完成时,这个文件将被移动到downloadDestinationPath 。
- - (IBAction)resumeInterruptedDownload:(id)sender
- {
- NSURL *url = [NSURL URLWithString:
- @"http://www.dreamingwish.com/wp-content/uploads/2011/10/asihttprequest-auth.png"];
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- NSString *downloadPath = @"/Users/ben/Desktop/asi.png";
- //当request完成时,整个文件会被移动到这里
- [request setDownloadDestinationPath:downloadPath];
- //这个文件已经被下载了一部分
- [request setTemporaryFileDownloadPath:@"/Users/ben/Desktop/asi.png.download"];
- [request setAllowResumeForFileDownloads:YES];
- [request startSynchronous];
- //整个文件将会在这里
- NSString *theContent = [NSString stringWithContentsOfFile:downloadPath];
- }
断点续传的工作原理是读取temporaryFileDownloadPath的文件的大小,并使用Range: bytes=x HTTP头来请求剩余的文件内容。
ASIHTTPRequest并不检测是否存在Accept-Ranges头(因为额外的HEAD头请求会消耗额外的资源),所以只有确定服务器支持断点续传下载时,再使用这个特性。
iOS开发ASIHTTPRequest直接读取磁盘数据流请求体
本文为大家介绍了iOS开发ASIHTTPRequest直接读取磁盘数据流的请求体的内容,其中包括ASIFormDataRequests,普通ASIHTTPRequest等等内容。
从0.96版本开始,ASIHTTPRequest可以使用磁盘上的数据来作为请求体。这意味着不需要将文件完全读入内存中,这就避免的当使用大文件时的严重内存消耗。使用这个特性的方法有好几种:ASIFormDataRequests和普通ASIHTTPRequest等等,下面来具体介绍。
ASIFormDataRequests
当使用setFile:forKey:时,ASIFormDataRequests自动使用这个特性。request将会创建一个包含整个post体的临时文件。文件会一点一点写入post体。这样的request是由 CFReadStreamCreateForStreamedHTTPRequest创建的,它使用文件读取流来作为资源。
- NSURL *url = [NSURL URLWithString:@"http://www.dreamingwish.com/"];
- ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
- [request setPostValue:@"foo" forKey:@"post_var"];
- [request setFile:@"/Users/ben/Desktop/bigfile.txt" forKey:@"file"];
- [request startSynchronous];
普通ASIHTTPRequest
如果你明白自己的request体会很大,那么为这个request设置流式读取模式。
- [request setShouldStreamPostDataFromDisk:YES];
下面的例子中,我们将一个NSData对象添加到post体。这有两个方法:从内存中添加(appendPostData:),或者从文件中添加(appendPostDataFromFile:);
- NSURL *url = [NSURL URLWithString:@"http://www.dreamingwish.com/"];
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setShouldStreamPostDataFromDisk:YES];
- [request appendPostData:myBigNSData];
- [request appendPostDataFromFile:@"/Users/ben/Desktop/bigfile.txt"];
- [request startSynchronous];
这个例子中,我们想直接PUT一个大文件。我们得自己设置setPostBodyFilePath ,ASIHTTPRequest将使用这个文件来作为post体。
- NSURL *url = [NSURL URLWithString:@"http://www.dreamingwish.com"];
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setRequestMethod:@"PUT"];
- [request setPostBodyFilePath:@"/Users/ben/Desktop/another-big-one.txt"];
- [request setShouldStreamPostDataFromDisk:YES];
- [request startSynchronous];
IMPORTANT:切勿对使用上述函数的request使用setPostBody——他们是互斥的。只有在你要自己建立request的请求体,并且还准备在内存中保持这个请求体时,才应该使用setPostBody。
iOS开发ASIHTTPRequest使用download cache
本文为大家介绍了iOS开发ASIHTTPRequest使用download cache的内容,其中包括cache策略,存储策略,其他cache相关的特性,编写自己的cache等等内容。
从1.8版本开始,ASIDownloadCache和ASICacheDelegate的API改变了,你可能需要修改你的代码。
尤其是,cache策略的可用选项发生了改变,你现在可以对单一request使用结合的cache策略
ASIHTTPRequest可以自动缓存下载的数据,在很多情况下这很有用。
- 当你离线时,你无法再次下载数据,而你又需要访问这些数据
- 从上次下载这些数据后,你只想在数据更新后才下载新的数据
- 你处理的数据永远不会发生改变,所以你只想下载一次数据
在之前版本的ASIHTTPRequest里,遇到上述情况,你只能自己处理这些策略。在一些情况下,使用download cache可以让你不用再写本地缓存机制。
ASIDownloadCache 是个简单的URL cache,可以用来缓存GET请求的相应数据。一个request要被缓存,它首先必须请求成功(没有发送错误),服务器必须返回200HTTP状态值。或者,从1.8.1版本开始,301,302,303,307重定向状态码都可以。
要打开响应值的cache机制很简单:
- [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]];
这样做以后,所有的request都会自动使用cache。如果你愿意,你可以让不同的request使用共享的cache:
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setDownloadCache:[ASIDownloadCache sharedCache]];
你不会被局限于使用单一的cache,你可以想创建多少cache就创建多少cache,只要你喜欢 ^ ^。当你自己创建一个cache,你必须设定cache的路径——这路径必须是一个你拥有写权限的目录。
- ASIDownloadCache *cache = [[[ASIDownloadCache alloc] init] autorelease];
- [cache setStoragePath:@"/Users/ben/Documents/Cached-Downloads"];
- //别忘了 - 你必须自己retaining你自己的cache!
- [self setMyCache:cache];
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setDownloadCache:[self myCache]];
cache策略
cache策略是你控制cache中信息的主要方法,控制何时使用cache数据而非重新下载数据。
每个request的cache策略可是由request的cachePolicy 属性来控制的。cache策略使用掩码来定义,所以你可以二进制“与”操作他们。
- // 每次都向服务器询问是否有新的内容可用,
- // 如果请求失败, 使用cache的数据,即使这个数据已经过期了
- [request setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];
你可以使用下列cache策略选项来控制request的缓存策略:
ASIUseDefaultCachePolicy 默认的cache 策略。请勿将这一项与其他项结合使用。当你设置一个request使用cache,它会使用cache的defaultCachePolicy. ASIDownloadCache的默认cache策略是‘ASIAskServerIfModifiedWhenStaleCachePolicy’.
ASIDoNotReadFromCacheCachePolicy 使用这一项,request将不会从cache中读取数据
ASIDoNotWriteToCacheCachePolicy 使用这一项,request将不会把数据存入cache
ASIAskServerIfModifiedWhen
StaleCachePolicy
这是ASIDownloadCaches的默认cache策略。使用这个策略时,request会先查看cache中是否有可用的缓存数据。如果没有,request会像普通request那样工作。
如果有缓存数据并且缓存数据没有过期,那么request会使用缓存的数据,而且不会向服务器通信。如果缓存数据过期了,request会先进行GET请求来想服务器询问数据是否有新的版本。如果服务器说缓存的数据就是当前版本,那么缓存数据将被使用,不会下载新数据。在这种情况下,cache的有效期将被设定为服务器端提供的新的有效期。如果服务器提供更新的内容,那么新内容会被下载,并且新的数据以及它的有效期将被写入cache。
ASIAskServerIfModifiedCachePolicy 这一项与ASIAskServerIfModifiedWhenStaleCachePolicy相同,除了一点:request将会每次都询问服务器端数据是否有更新。
ASIOnlyLoadIfNotCachedCachePolicy 使用这一项,cache数据将一直被使用,无视过期时间
ASIDontLoadCachePolicy 使用这一项时,只有当响应数据有缓存时,request才会成功。如果一个request没有缓存的响应数据,那么这个request将会停止,并且不会有错误设置在request上。
ASIFallbackToCacheIf
LoadFailsCachePolicy
当使用这一项时,当request失败时,request会回头请求cache数据。如果请求失败后,request使用的cache数据,那么这个request会成功(没有错误)。你通常会将这一项与其他项结合使用,因为它适用于指定当发生错误时request的行为。
当你设定了一个cache对象的defaultCachePolicy 属性,所有使用这个cache对象的request都会使用这个cache策略,除非你为request设置了另外的策略。
存储策略
存储策略允许你定义一个cache可以存储特定的相应数据多久。ASIHTTPRequest目前支持两种存储策略:
ASICacheForSessionDurationCacheStoragePolicy是默认值。相应数据只会在会话期间被存储,在第一次使用cache时,或者在调用 [ASIHTTPRequest clearSession]时,数据会被清除。
使用ASICachePermanentlyCacheStoragePolicy,缓存的相应数据会被永久存储。要使用这个存储策略,向request设置:
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
要手动清除cache,调用函数clearCachedResponsesForStoragePolicy:,传入要清除的cache数据的存储策略:
- [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
其他cache相关的特性
- // 当你关闭 shouldRespectCacheControlHeaders,cache对象会存储响应数据,而无视
- // 服务器的显式“请勿缓存”声明 (例如:cache-control 或者pragma: no-cache 头)
- [[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:NO];
- // 可以设定request的secondsToCache来覆盖服务器设定的内容有效期, 这时,响应数据
- // 会一直被缓存,直到经过secondsToCache秒
- ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
- [request setSecondsToCache:60*60*24*30]; // 缓存30 天
- //当request开始执行后,如果响应数据是从缓存中取得的,didUseCachedResponse 会返回YES
- [request didUseCachedResponse];
- // 向cache对象索取一个路径来存储相应数据. 这是使用download cache的最有效率的方法,
- // 因为此时,当request完成后,数据不需要被复制到cache中.
- [request setDownloadDestinationPath:
- [[ASIDownloadCache sharedCache] pathToStoreCachedResponseDataForRequest:request]];
编写自己的cache
如果你已经持有一个download cache并且想将他插入ASIHTTPRequest中,或者你喜欢自己写自己的download cache,那么让你的cache实现ASICacheDelegate协议。