使用NSOperation+NSOperationQueue实现多任务网络连接
在网络应用程序中,经常需要多任务连接来提高程序的性能。比如多任务下载,多任务HTTP请求等,即线程控制模型中的工作群模型。使用 NSOperation 可以很容易实现这个功能。下面就以使用NSOperation处理并行的HTTP请求为例子,说明其用法。
首先准备一个 NSOperation 的子类,用于处理 HTTP 请求。
1 @interface RequestOperation : NSOperation { 2 NSURLRequest* _request; 3 NSMutableData* _data; 4 } 5 6 - (id)initWithRequest:(NSURLRequest *)request; 7 8 @end
下面是实现:
1 @implementation RequestOperation 2 3 - (id)initWithRequest:(NSURLRequest *)request { 4 if (self = [self init]) { 5 _request = [request retain]; 6 _data = [[NSMutableData data] retain]; 7 } 8 return self; 9 } 10 11 - (void)dealloc { 12 [_request release]; 13 [_data release]; 14 [super dealloc]; 15 } 16 17 // 如果不载下面的函数,会出错 18 - (BOOL)isConcurrent { 19 return YES; 20 } 21 22 // 开始处理 23 - (void)start { 24 if (![self isCancelled]) { 25 // 以异步方式处理事件 26 [NSURLConnection connectionWithRequest:_request delegate:self]; 27 } 28 } 29 30 // 取得数据 31 - (void)connection:(NSURLConnection*)connection 32 didReceiveData:(NSData*)data { 33 // 添加数据 34 [_data appendData:data]; 35 } 36 37 // HTTP请求结束 38 - (void)connectionDidFinishLoading:(NSURLConnection*)connection { 39 } 40 41 @end
如果没有重载 isConcurrent 函数,缺省是返回NO,就是说只能以同步的方式处理。而如果又使用了connectionWithRequest:delegate: 以异步方式处理事件后,会产生下面的错误信息:
_NSAutoreleaseNoPool(): Object 0x18a140 of class NSURLConnection autoreleased with no pool in place - just leaking
然后在你的 Controller 类中用 NSOperationQueue 来处理各个任务。
1 @interface xxViewController : UIViewController { 2 NSOperationQueue* _queue; 3 } 4 5 @end
1 @implementation xxViewController 2 3 - (IBAction)buttonClicked:(id) sender { 4 _queue = [[NSOperationQueue alloc] init]; 5 // 第一个请求 6 NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]]; 7 RequestOperation* operation = [[RequestOperation alloc] initWithRequest:request]; 8 [operation autorelease]; 9 // 第二个请求 10 NSURLRequest* request2 = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.yahoo.co.jp"]]; 11 RequestOperation* operation2 = [[RequestOperation alloc] initWithRequest:request2]; 12 [operation2 autorelease]; 13 // 开始处理 14 [_queue addOperation:operation]; 15 } 16 17 @end
以上,用 NSOperation 来并行处理不同的任务,使用 NSOperationQueue 来控制复数的 NSOperation,并且可以限制Queue的大小,而不是无限制的使用任务。当一个任务完成,就执行待机中的任务。
经过测试,上面的方法不可行,因为在线程中无法使用异步的方式去请求http
改过之后的例子如下
1 // RequestOperation.m 2 3 #import "RequestOperation.h" 4 5 6 @implementation RequestOperation 7 8 - (id)initWithURL:(NSString *)url 9 { 10 data = [[NSMutableData data] retain]; 11 self = [super init]; 12 if (self) 13 { 14 targetURL = url; 15 } 16 return self; 17 } 18 19 - (void)showData:(NSData *)theData encoding:(NSString *)encoding { 20 if (data != theData) { 21 [data release]; 22 data = [theData retain]; 23 24 // NSURLResponse's encoding is an IANA string. Use CF utilities to convert it to a CFStringEncoding then a NSStringEncoding 25 NSStringEncoding nsEncoding = NSUTF8StringEncoding; // default to UTF-8 26 if (encoding) { 27 CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)encoding); 28 if (cfEncoding != kCFStringEncodingInvalidId) { 29 nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); 30 } 31 } 32 NSString *displayString = [[NSString alloc] initWithData:data encoding:nsEncoding]; 33 //NSLog(@"body:%@", displayString); 34 [displayString release]; 35 } 36 } 37 38 - (void)main { 39 NSLog(@"url:%@", targetURL); 40 // Synchronously grab the data 41 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:targetURL]]; 42 NSError *error; 43 NSURLResponse *response; 44 NSData *result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 45 [self showData:result encoding:[response textEncodingName]]; 46 if (!result) { 47 NSLog(@"error:",[error localizedDescription]); 48 } 49 NSLog(@"%@ is download.", targetURL); 50 } 51 52 - (void)dealloc { 53 [data release]; 54 [super dealloc]; 55 } 56 57 @end 58 59 界面部分代码 60 NSOperationQueue* _queue; 61 - (IBAction)buttonClicked:(id) sender { 62 NSLog(@"start."); 63 _queue = [[NSOperationQueue alloc] init]; 64 [_queue setMaxConcurrentOperationCount:2]; 65 RequestOperation* operation1 = [[RequestOperation alloc] initWithURL:@"http://www.google.com"]; 66 [operation1 autorelease]; 67 RequestOperation* operation2 = [[RequestOperation alloc] initWithURL:@"http://www.yahoo.co.jp"]; 68 [operation2 autorelease]; 69 RequestOperation* operation3 = [[RequestOperation alloc] initWithURL:@"http://new.sina.com.cn"]; 70 [operation3 autorelease]; 71 RequestOperation* operation4 = [[RequestOperation alloc] initWithURL:@"http://www.163.com"]; 72 [operation4 autorelease]; 73 RequestOperation* operation5 = [[RequestOperation alloc] initWithURL:@"http://www.sohu.com"]; 74 [operation5 autorelease]; 75 RequestOperation* operation6 = [[RequestOperation alloc] initWithURL:@"http://www.csdn.net"]; 76 [operation6 autorelease]; 77 RequestOperation* operation7 = [[RequestOperation alloc] initWithURL:@"http://www.yahoo.com.tw"]; 78 [operation7 autorelease]; 79 // 开始处理 80 [_queue addOperation:operation1]; 81 [_queue addOperation:operation2]; 82 [_queue addOperation:operation3]; 83 [_queue addOperation:operation4]; 84 [_queue addOperation:operation5]; 85 [_queue addOperation:operation6]; 86 [_queue addOperation:operation7]; 87 NSLog(@"end."); 88 }