I've got the problem when I tried to do asynchronous requests to server from background thread. I've never got results of those requests. Simple example which shows the problem:
@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
Then I call this from main thread
asyncImgRequest = [[AsyncImgRequest alloc] init];
asyncImgRequest.delegate = self;
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
method downloadImage is listed below:
-(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];
}
The problem is that method imageDownloadDidFinish is never called. Moreover none of methods
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response
are called. However if I replace
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
by
[self performSelector:@selector(downloadImage) withObject:nil];
everything is working correct. I assume that the background thread dies before async request is finished it job and this causes the problem but I'm not sure. Am I right with this assumptions? Is there any way to avoid this problem?
I know I can use sync request to avoid this problem but it's just simple example, real situation is more complex.
Thanks in advance.
Yes, the thread is exiting. You can see this by adding:
-(void)threadDone:(NSNotification*)arg
{
NSLog(@"Thread exiting");
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(threadDone:)
name:NSThreadWillExitNotification
object:nil];
You can keep the thread from exiting with:
-(void) downloadImage
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[self downloadImage:urlString];
CFRunLoopRun(); // Avoid thread exiting
[pool release];
}
However, this means the thread will never exit. So you need to stop it when you're done.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
CFRunLoopStop(CFRunLoopGetCurrent());
}
Learn more about Run Loops in the Threading Guide and RunLoop Reference.