Question

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

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top