I am trying to follow the XMLPerformance example to make an xml parser of my own. So far I’m having the hardest time making autorelease pools work, I get a crash the instant I recreate a pool.
I narrowed down the issue to this test case:
PoolCrashTest.h
#import <SenTestingKit/SenTestingKit.h>
@interface PoolCrashTest : SenTestCase
{
@private
NSURLConnection *connection;
NSAutoreleasePool *downloadAndParsePool;
BOOL done;
}
@property (nonatomic, retain) NSURLConnection *connection;
@property (nonatomic, assign) NSAutoreleasePool *downloadAndParsePool;
- (void)downloadAndParse:(NSURL *)url;
@end
PoolCrashTest.m
#import "PoolCrashTest.h"
@implementation PoolCrashTest
@synthesize downloadAndParsePool, connection;
- (void)downloadAndParse:(NSURL *)url {
done = NO;
self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
self.connection = [[NSURLConnection alloc]
initWithRequest:theRequest delegate:self];
if (connection != nil) {
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
} while (!done);
}
self.connection = nil;
[downloadAndParsePool release];
self.downloadAndParsePool = nil;
}
#pragma mark NSURLConnection Delegate methods
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data {
[downloadAndParsePool drain];
crash after this line ^
self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
}
- (void)testPoolCrash
{
NSURL *dumpURL = [NSURL URLWithString:@"file:///some.xml"];
[NSThread detachNewThreadSelector:@selector(downloadAndParse:)
toTarget:self withObject:dumpURL];
sleep(10);
}
@end
Can someone explain how to properly purge autorelease pool in a NSURLConnection delegate running in a thread?
I have tried to follow XMLPerformance as close as possible… I’m targeting Lion with mostly default project settings.
craig is correct when he says you’re over-releasing your pool. In a non-GC environment,
releaseanddrainhave the same effect. In a GC environment,releaseis a no-op for any object, sodrainmust be used instead. I’d just usedrain.However,
NSAutoreleasePoolobjects aren’t really things you should be making a property of your class; they’ll work best for you if you restrict their use to a lexical scope. There are a couple of ways you could use a pool in the code you posted above which would be sufficient.Remember when you’re spinning your run loop that it’s going to be popping in and out of the call to run the run loop in the common modes; so you could do this instead:
and you’d be draining any autoreleased objects created in the particular turn of the run loop. Because the connection’s delegate callback will be called due to this invocation of the run loop, any autoreleased objects created in the delegate callback will be cleaned up when this pool drains.
If you’re not comfortable with this, you could place a pool inside your delegate method depending on how much work your delegate method is likely to be doing:
And in your case, it would have roughly the same effect.
I would strongly recommend doing something like my first example above and eliminating the autorelease pool behavior you have right now. Keeping
NSAutoreleasePoolobjects to a single lexical scope facilitates everything from debugging to safe exception handling when it becomes necessary.