iOS HTTP请求——同步请求、队列式异步请求、异步请求

在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)解析器来渐进解析响应数据,那么不要使用队列式异步请求。
  • 如果需要在请求完成前取消,不要使用队列式异步请求。
    -(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];
                }
                
            }
        }];
    }
    上述代码中,如果HTTP的状态码为200,说明请求成功,并将返回的数据解析到NSDictionary对象中,然后,代码验证所提供的委托类支持setVideo:方法,如果支持,就会在主线程中执行该方法。
三、异步请求
异步请求使用的对象与同步和队列式异步请求相同,另外又增加了一个对象:NSURLConnectionDelegate。协议处理器在HTTP协议过程中处理时,会在连接的重要阶段调用委托方法,如下图所示。
iOS HTTP请求——同步请求、队列式异步请求、异步请求_第1张图片
异步请求的使用场景:
  • 对于大数据的上传或下载,使用异步请求以减少内存使用;
  • 在需要认证的情况下使用异步请求;
  • 如果需要进度条,使用异步请求;
  • 在后台线程上使用异步请求时,需要提供一个运行循环;
  • 对于可以在后台线程的请求队列中轻松调度和完成的简单请求来说,不必使用异步请求;
  • 使用流(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响应对象时,调用该方法。注意:该方法可能会被协议处理器调用多次,因此,代码必须处理重新开始这一场景。


[未完待续。。。]

你可能感兴趣的:(iOS学习笔记)