NSOperation(s) leaks only on iOS 3 device
I have some NSOperations subclasses that handle CoreData imports. I believe i've ticked most of the non-main thread issues
- I create my own autorelease pool in the
main
method - I create a
NSManagedObjectContext
for each operation
These operations are loaded into a NSOperationQueue, with the maximum number of concurrent operations set 1.
The code works perfectly on a iOS 4.0.1, however on a iOS 3.1.3 device a get a lot of log messages like the following
*** _NSAutoreleaseNoPool(): Object 0x5f926c0 of class NSCFDictionary autoreleased with no pool in place - just leaking
NSOperation Subclass main method
-(void) main{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@try {
//Start Operation
//====================================
NSManagedObjectID *objID = nil;
NSError *err = nil;
id user = nil;
if( !(userID = [self __lookup:[self userID] inContext: [self threadContext]]) ){
//Set the name of the element
user = [[self threadContext] objectWithID:objID];
//Update the name
[user setValue:@"John Doe" forKey:@"name"];
[user setValue:@"Hello world" forKey:@"status"];
}
if( ![[self threadContext] save:&err] ){
DebugLog(@"Couldn't savechanges %@", err);
}
//====================================
//End Operation
}
@catch (NSException * e) {
DebugLog(@"Exception %@",e);
}
//====================================
[pool drain];
}
The __lookup:inContext:
method
-(NSManagedObjectID*) __lookup:(id)aID inContext:(NSManagedObjectContext*) aContext{
NSPredicate *predicate = nil;
NSEntityDescription *entity;
NSFetchRequest *fetchRequest = nil;
NSError *err = nil;
predicate = [NSPredicate predicateWithFormat:@"userID == %@",aID];
entity = [NSEntityDescription entityForName:@"开发者_运维知识库User" inManagedObjectContext:aContext];
fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setPredicate:predicate];
[fetchRequest setEntity:entity];
//Only fetch id's for speed
[fetchRequest setResultType:NSManagedObjectIDResultType];
return [[aContext executeFetchRequest:fetchRequest error:&err] lastObject];
}
Most of the other methods instance methods, ie threadContext
look similar to the __lookup:inContext:
method. I'm aware that i don't create Autorelease pools for the instance methods, but according to my understanding of how autorelease works, as long as these methods are only called inside the main method, after the NSAutoreleasePool
has been created, the outer most pool should be used. I create objects such as the NSManagedObjectContext lazily, and in most cases don't use the start
method
Solved it, This operation was launching a NSURLConnection using the advice in an article by Dave Dribin. However, where possible i try to never cut and paste other peoples code, so i can always filter what i am putting into my own code.
Turns out i forgot to add this check
if( ![NSThread isMainThread] ){
[self performSelectorOnMainThread:@selector(start)
withObject:nil
waitUntilDone:NO];
return;
}
Which ensures that the start
method runs on the main thread, where a NSAutoreleasePool
already exists. Simple mistake, easy solution.
精彩评论