从后台线程到服务器的asynchronous请求

当我试图从后台线程对服务器执行asynchronous请求时,我遇到了这个问题。 我从来没有得到这些要求的结果。 显示问题的简单例子:

@protocol AsyncImgRequestDelegate -(void) imageDownloadDidFinish:(UIImage*) img; @end @interface AsyncImgRequest : NSObject { NSMutableData* receivedData; id<AsyncImgRequestDelegate> delegate; } @property (nonatomic,retain) id<AsyncImgRequestDelegate> delegate; -(void) downloadImage:(NSString*) url ; @end @implementation AsyncImgRequest -(void) downloadImage:(NSString*) url { NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0]; NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; if (theConnection) { receivedData=[[NSMutableData data] retain]; } else { } } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { [delegate imageDownloadDidFinish:[UIImage imageWithData:receivedData]]; [connection release]; [receivedData release]; } @end 

然后我从主线程调用这个

 asyncImgRequest = [[AsyncImgRequest alloc] init]; asyncImgRequest.delegate = self; [self performSelectorInBackground:@selector(downloadImage) withObject:nil]; 

方法downloadImage列出如下:

 -(void) downloadImage { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [asyncImgRequest downloadImage:@"http://photography.nationalgeographic.com/staticfiles/NGS/Shared/StaticFiles/Photography/Images/POD/l/leopard-namibia-sw.jpg"]; [pool release]; } 

问题是,方法imageDownloadDidFinish永远不会被调用。 而且没有任何方法

 - (void)connectionDidFinishLoading:(NSURLConnection *)connection - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response 

叫做。 但是,如果我更换

  [self performSelectorInBackground:@selector(downloadImage) withObject:nil]; 

通过

  [self performSelector:@selector(downloadImage) withObject:nil]; 

一切工作正确。 我假设后台线程在asynchronous请求完成之前死了它的工作,这导致了这个问题,但我不知道。 我对这个假设是否正确? 有什么办法可以避免这个问题?

我知道我可以使用同步请求来避免这个问题,但这只是一个简单的例子,实际情况更复杂。

提前致谢。

是的,线程正在退出。 你可以通过添加:

 -(void)threadDone:(NSNotification*)arg { NSLog(@"Thread exiting"); } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadDone:) name:NSThreadWillExitNotification object:nil]; 

你可以保持线程退出:

 -(void) downloadImage { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [self downloadImage:urlString]; CFRunLoopRun(); // Avoid thread exiting [pool release]; } 

但是,这意味着线程将永远不会退出。 所以当你完成的时候你需要停下来。

 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { CFRunLoopStop(CFRunLoopGetCurrent()); } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { CFRunLoopStop(CFRunLoopGetCurrent()); } 

详细了解线程指南和RunLoop参考中的运行循环。

您可以在后台线程上启动连接,但必须确保在主线程上调用委托方法。 这是无法完成的

 [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self]; 

因为它立即开始。

这样做来configuration委托队列,甚至在辅助线程上也可以工作:

 NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:NO]; [connection setDelegateQueue:[NSOperationQueue mainQueue]]; [connection start]; 

无论如何NSURLRequests是完全asynchronous的。 如果你需要从主线程以外的线程创build一个NSURLRequest,我认为最好的办法就是从主线程 创buildNSURLRequest

 // Code running on _not the main thread_: [self performSelectorOnMainThread:@selector( SomeSelectorThatMakesNSURLRequest ) withObject:nil waitUntilDone:FALSE] ; // DON'T block this thread until the selector completes. 

所有这一切都是从主线程的HTTP请求(这样它实际上工作,并不神秘消失)。 HTTP响应将像往常一样回到callback中。

如果你想用GCD做这个,你可以走了

 // From NOT the main thread: dispatch_async( dispatch_get_main_queue(), ^{ // // Perform your HTTP request (this runs on the main thread) } ) ; 

MAIN_QUEUE在主线程上运行。

所以我的HTTP获取函数的第一行如下所示:

 void Server::get( string queryString, function<void (char*resp, int len) > onSuccess, function<void (char*resp, int len) > onFail ) { if( ![NSThread isMainThread] ) { warning( "You are issuing an HTTP request on NOT the main thread. " "This is a problem because if your thread exits too early, " "I will be terminated and my delegates won't run" ) ; // From NOT the main thread: dispatch_async( dispatch_get_main_queue(), ^{ // Perform your HTTP request (this runs on the main thread) get( queryString, onSuccess, onFail ) ; // re-issue the same HTTP request, // but on the main thread. } ) ; return ; } // proceed with HTTP request normally }