views:

4046

answers:

2

I have a class that updates two .plist files in the app documents directory via an NSURLConnection. The class acts as its own delegate for NSURLConnection. It works properly when I ask for a single file, but fails when I try to update two files. Does it look like I should start a new thread for each of the getNewDatabase messages?

- (void)getAllNewDatabases {
    [self performSelectorOnMainThread:@selector(getNewDatabase:) withObject:@"file1" waitUntilDone:YES];
    [self performSelectorOnMainThread:@selector(getNewDatabase:) withObject:@"file2" waitUntilDone:YES];
}

- (BOOL)getNewDatabase:(NSString *)dbName
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSMutableString *apiString = [[NSMutableString alloc] initWithString:kAPIHost];
    [apiString appendFormat:@"/%@.plist",dbName];
    NSURLRequest *myRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:apiString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    NSURLConnection *myConnection = [[NSURLConnection alloc] initWithRequest:myRequest delegate:self];
    [apiString release];
    if( myConnection )
    {
        //omitted for clarity here
    }
    [pool release];
}
//NSURLConnection delegate methods here ...
+1  A: 

I found something interesting with NSURLConnection and NSThread - the thread will only live as long as it takes to perform the method that you call from it.

In the case above the thread will live only as long as getNewDatabase:(NSString *)dbName takes to complete, therefore killing off any of its delegate methods before they actually have time to do anything.

I found this website that gives a better explanation and a solution to the problem

I tweaked it a little bit so I could have a custom time out if it didn't complete in a given time frame (handy when someone is walking around between access points)

    start = [NSDate dateWithTimeIntervalSinceNow:3];

    while(!isFinished && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                                                  beforeDate:[NSDate distantFuture]]){

 if([start compare:[NSDate date]] == NSOrderedAscending){
  isFinished = YES;
 }
}
James Raybould
+3  A: 

As it stands currently in the code you provided, getNewDatabase: is running on the main thread of your application. The problem in this particular case then is something other than the life cycle of the thread, as James observed in his case.

If you did intend to perform this operation in the background, I'd recommend looking into using NSOperationQueue and NSOperation rather than solving the problem with the current code. I think your case is a great fit for NSOperationQueue, especially given that you have more than one download task to perform.

Dave Dribin has an excellent article about using asynchronous API, such as NSURLConnection, inside an NSOperation. Alternatively, as long as you're running in a background thread, you can also simplify the process and just use a synchronous API method instead in your NSOperation, such as initWithContentsOfURL:.

Marcus Zarra has also written a tutorial that demonstrates how easy it is to incorporate and use NSOperationQueue for simple background operations.

Sean Murphy
Thanks - in the meanwhile - I forked a version that does exactly that with NSOperation / NSOperationQueue. Works perfectly now.
alan