在iOS应用与HTTP服务器进行HTTP通信时,有3个主要的方法可以执行HTTP请求并接收响应。
- 同步请求——该方法为阻塞式,直到整个响应加载完毕并返回调用方法为止。
- 异步请求——该请求运行在起始线程中,不过在请求处理时会调用委托方法。
- 队列式异步——将请求放到一个队列中以在后台线程中执行。
上述3类请求共用4类对象:NSURL、NSURLRequest、NSURLConnection、NSURLResponse。
1、NSURL
通常,创建NSURL对象使用类方法:urlWithString
NSURL *url = [NSURL urlWithString:@"..."];
NSData *data = [NSData dataWithContentOfURL:url];
注意,NSURL对象是不可变的,因此无法先创建空得NSURL对象再通过调用对象的赋值方法来装配其属性,对象要么通过另一个NSURL对象、要么通过NSString对象进行实例化。2、NSURLRequest
NSURLRequest对象包含了加载URL内容所需的信息,且独立于URL中指定的协议。在iOS中得URL加载系统支持HTTP、https、FTP和FILE URL内容的加载。
创建NSURLRequest对象最简单的方式是使用类方法和NSURL对象:
NSURL *url = [NSURL urlWithString:@"..."];
NSRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
其中,cachePolicy:NSURLRequestReloadIgnoringCacheData指定了缓存策略,timeoutInterval指定了超时时间。NSMutableURLRequest是NSURLRequest的子类,它提供了赋值方法以修改请求的属性。
NSURL *url = [NSURL urlWithString:@"..."];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[@"Post body" dataUsingEncoding:NSUTF8StringEncoding]];
3、NSURLConnection该对象是URL加载系统活动的中心,但只提供用于初始化、开启和取消连接的方法。
4、NSURLResponse
该对象会在URL加载请求完毕后返回。
一、同步请求
该请求实现最简单,不过其代价是应用灵活性的降低。发出同步请求时,请求所处的线程会阻塞,直至请求完成或失败为止。
使用同步请求时需要注意:
- 只在后台线程中使用同步请求,除非确定请求访问的是本地资源,否则不要在主线程上使用;
- 只有在确定返回的数据不会超出应用的内存时才使用,因为整个响应体是位于应用内存中,如果响应数据很大,可能会导致应用内存溢出的问题;
- 在处理返回的数据前,验证错误与调用返回的HTTP响应状态码;
- 源URL需要验证,那么不要使用同步请求;
- 因为请求是原子的,如果需要向用户提供进度条,不要使用同步请求;
- 如果需要通过流(stream)解析器来渐进解析响应数据,那么不要使用同步请求;
- 如果需要在请求完成前取消,不要使用同步请求。
-(NSArray *) doSyncRequest:(NSString *)urlStr{
NSURL *url = [NSURL URLWithString:urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10.0];
NSHTTPURLResponse *response;
NSError *err = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
NSDictionary *dict = [XMLReader dictionaryForXMLData:data error:&err];
NSArray *entries = [self getEntriesArray:dict];
return entries;
}
二、队列式异步请求
该请求类似于同步请求,它们的主要区别在于URL加载执行的队列式异步请求位于队列中,可能在后台线程中。在创建队列式异步请求前,首先需要创建队列,队列中是执行的请求。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
操作队列可以并发执行多个操作,一般地,并发操作数量由iOS系统决定,也可以调用setMaxConcurrentOperationCount:方法来重写默认值。
当应用启动时,一个队列就会自动创建,可以通过调用NSOperationQueue的mainQueue类方法来获取该队列,但是需要注意 不要在该队列执行网络请求,这样会阻塞主线程。
- 与同步请求相同,只有在知道返回数据的大小不会超出应用内存时,才使用队列式异步请求。
- 为所有操作使用单一的NSOperationQueue,根据服务器的能力以及预期的网络状况控制当前操作的最大数量。
- 在处理返回的数据前验证错误与调用返回的HTTP响应状态码。
- 如果源URL需要验证,则不要使用队列式异步请求。
- 如果需要提供进度条,则不要使用队列式异步请求。
- 如果需要通过流(stream)解析器来渐进解析响应数据,那么不要使用队列式异步请求。
- 如果需要在请求完成前取消,不要使用队列式异步请求。
上述代码中,如果HTTP的状态码为200,说明请求成功,并将返回的数据解析到NSDictionary对象中,然后,代码验证所提供的委托类支持setVideo:方法,如果支持,就会在主线程中执行该方法。-(void) doQueueAsyncRequest:(NSString *)urlStr delegate:(id)delegate{ NSURL *url = [NSURL URLWithString:urlStr]; NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10.0]; if (queue == nil) { queue = [[NSOperationQueue alloc] init]; } [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *err){ if (err != nil) { }else{ if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; if (httpResponse.statusCode != 200) { return ; } } NSDictionary *dict = [XMLReader dictionaryForXMLData:data error:&err]; NSArray *entries = [self getEntriesArray:dict]; if ([delegate respondsToSelector:@selector(setVideos:)]) { [delegate performSelectorOnMainThread:(setVideos:) withObject:entries waitUntilDone:YES]; } } }]; }
三、异步请求
异步请求使用的对象与同步和队列式异步请求相同,另外又增加了一个对象:NSURLConnectionDelegate。协议处理器在HTTP协议过程中处理时,会在连接的重要阶段调用委托方法,如下图所示。
异步请求的使用场景:
- 对于大数据的上传或下载,使用异步请求以减少内存使用;
- 在需要认证的情况下使用异步请求;
- 如果需要进度条,使用异步请求;
- 在后台线程上使用异步请求时,需要提供一个运行循环;
- 对于可以在后台线程的请求队列中轻松调度和完成的简单请求来说,不必使用异步请求;
- 使用流(stream)上传数据时,实现connection:newBodyStream方法以避免对输入流的复制。
下列代码示例表述了使用异步技术初始化URL加载请求:
1、创建NSURL对象,
2、构建请求(NSRequest),
3、请求创建完毕后,创建NSURLConnection对象并将自身作为委托对象。
-(void)start{
NSURL *url = [NSURL URLWithString:srcURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
[self.conn start];
}
下面看看上图所示的几个委托方法1、connection:willSendRequest:redirectResponse:
该方法在连接接收到HTTP重定向响应时调用。如果接收到来自服务器的重定向请求,就会调用该方法。由于请求可以执行多个重定向,因此对于单个请求而言,该方法可能不会被调用,也可能被多次调用。如果代理没有实现该方法,那么协议处理器会将重定向转向新的位置。通过实现该方法,代码可以拦截重定向,并根据重定向的特点终止连接或修改请求等。
-(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response{
NSLog(@"Redirect request for %@ redirecting to %@", srcURL, request.URL);
NSLog(@"All headers = %@", [(NSHTTPURLResponse *)redirectResponse allHeaderFields]);
// 重定向
return request;
}
2、connection:didReceiveResponse:
收到足够的数据创建URL响应对象时,调用该方法。注意:该方法可能会被协议处理器调用多次,因此,代码必须处理重新开始这一场景。
[未完待续。。。]