views:

5772

answers:

7

I have an method which save files to the internet, it works but just slow. Then I'd like to make the user interface more smooth, so I create an NSThread to handle the slow task.

I am seeing a list of errors like:

_NSAutoreleaseNoPool(): Object 0x18a140 of class NSCFString autoreleased with no pool in place - just leaking

Without NSThread, I call the method like:

[self save:self.savedImg];

And I used the following to use NSThread to call the method:

NSThread* thread1 = [[NSThread alloc] initWithTarget:self
                                        selector:@selector(save:)
                                              object:self.savedImg];
[thread1 start];

Thanks.

A: 

Within the thread, you need to create a new autorelease pool before you do anything else, otherwise the network operations will have issues as you saw.

Kendall Helmstetter Gelner
+1  A: 

You need to mainly create an autorelease pool for the thread. Try changing your save method to be like this:

- (void) save:(id)arg {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    //Existing code

    [pool drain];
}

You will not you that the above does not call release on the NSAutoreleasePool. This is a special case. For NSAutoreleasePool drain is equivalent to release when running without GC, and converts to a hint to collector that it might be good point to run a collection.

Louis Gerbarg
A: 

The memory leak is gone, but seems the URL connection does not work. The request does not go through, and no call back to the delegate.

NSURLConnection *connectionResponse = [[NSURLConnection alloc] initWithRequest:postRequest delegate:self];

If I don't use mutltiple threading call back fine, but with multiple thread, no call back.

Any more suggestion?

Thanks.

BlueDolphin
+2  A: 

You may need to create a run loop. I will add to Louis's solution:

BOOL done = NO;

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSRunLoop currentRunLoop];

// Start the HTTP connection here. When it's completed,
// you could stop the run loop and then the thread will end.

do {
 SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
 if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) {
  done = YES;
 }
} while (!done);

[pool release];
Chris Lundie
+7  A: 

Well first of all, you are both creating a new thread for your saving code and then using NSUrlConnection asynchronously. NSUrlConnection in its own implementation would also spin-off another thread and call you back on your newly created thread, which mostly is not something you are trying to do. I assume you are just trying to make sure that your UI does not block while you are saving...

NSUrlConnection also has synchronous version which will block on your thread and it would be better to use that if you want to launch your own thread for doing things. The signature is

+ sendSynchronousRequest:returningResponse:error:

Then when you get the response back, you can call back into your UI thread. Something like below should work:

- (void) beginSaving {
   // This is your UI thread. Call this API from your UI.
   // Below spins of another thread for the selector "save"
   [NSThread detachNewThreadSelector:@selector(save:) toTarget:self withObject:nil];    

}

- (void) save {
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  

   // ... calculate your post request...
   // Initialize your NSUrlResponse and NSError

   NSUrlConnection *conn = [NSUrlConnection sendSyncronousRequest:postRequest:&response error:&error];
   // Above statement blocks until you get the response, but you are in another thread so you 
   // are not blocking UI.   

   // I am assuming you have a delegate with selector saveCommitted to be called back on the
   // UI thread.
   if ( [delegate_ respondsToSelector:@selector(saveCommitted)] ) {
    // Make sure you are calling back your UI on the UI thread as below:
    [delegate_ performSelectorOnMainThread:@selector(saveCommitted) withObject:nil waitUntilDone:NO];
   }

   [pool release];
}
keremk
the case and signature is close on the sync call it should be NSURLConnection *conn = [NSURLConnection sendSyncronousRequest:postRequest returningResponse:
Jehiah
A: 

Thanks, this solution works!

BlueDolphin
A: 

I don't see any reason for you to use threads for this. Simply doing it asynchronously on the run loop should work without blocking the UI.

Trust in the run loop. It's always easier than threading, and is designed to provide the same result (a never-blocked UI).

Peter Hosey