views:

519

answers:

2

Hi, I have a CoreDataHelper static class that consists of 2 static methods. I ran my project through Clang and didn't find any leaks, however running through Instruments shows a leak that looks like it's related to CoreData. I have basic knowledge of memory management in objective-c and I believe I'm following the rules. However, I also think it's more likely there's something wrong with my code rather than a bug in Apple's CoreData stack. I'm running on the latest, Snow Leopard, iPhone SDK 3.1, XCode 3.2.

stack trace:

17 UIKit 528 Bytes -[UIApplication _run]

16 UIKit 64 Bytes -[UIApplication sendEvent:]

15 UIKit 64 Bytes -[UIApplication handleEvent:withNewEvent:]

14 UIKit 64 Bytes -[UIApplication _runWithURL:sourceBundleID:]

13 UIKit 16 Bytes -[UIApplication _performInitializationWithURL:sourceBundleID:]

12 helm 16 Bytes -[helmAppDelegate applicationDidFinishLaunching:] Classes/helmAppDelegate.m:113

11 helm 16 Bytes +[CoreDataHelper entityWithUIDexists:::] Classes/CoreDataHelper.m:50

10 helm 16 Bytes +[CoreDataHelper searchObjectsInContextCopy:::::] Classes/CoreDataHelper.m:39

9 CoreData 16 Bytes -[NSManagedObjectContext executeFetchRequest:error:]

8 CoreData 16 Bytes -[NSPersistentStoreCoordinator(_NSInternalMethods) executeRequest:withContext:]

7 CoreData 16 Bytes -[NSSQLCore executeRequest:withContext:]

6 CoreData 16 Bytes -[NSSQLiteConnection connect]

5 CoreData 16 Bytes -[NSSQLConnection createSchema]

4 CoreData 16 Bytes -[NSSQLConnection createTablesForEntities:]

3 CoreData 16 Bytes -[NSSQLConnection createTableForEntity:]

2 CoreData 16 Bytes -[NSSQLAdapter newCreateTableStatementForEntity:]

1 Foundation 16 Bytes -[NSCFString appendFormat:]

0 CoreFoundation 16 Bytes -[NSObject respondsToSelector:]


appdelegate:

BOOL b=[CoreDataHelper entityWithUIDexists:@"AddressBook" :context :[NSNumber numberWithInt:1]];

CoreDataHelper:

+(NSMutableArray *) searchObjectsInContextCopy: (NSString*) entityName : (NSManagedObjectContext *) managedObjectContext : (NSPredicate *) predicate : (NSString*) sortKey : (BOOL) sortAscending 
{
    NSLog(@"searchObjectsInContext");
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
    [request setEntity:entity]; 

    // If a predicate was passed, pass it to the query
    if(predicate != nil)
    {
     [request setPredicate:predicate];
    }

    // If a sort key was passed, use it for sorting.
    if(sortKey != nil)
    {
     //NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:sortAscending selector: @selector(caseInsensitiveCompare:)];
     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:sortAscending selector: @selector(caseInsensitiveCompare:)];
     NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
     [request setSortDescriptors:sortDescriptors];
     [sortDescriptors release];
     [sortDescriptor release];
    }

    NSError *error;

    NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];

    [request release];

    return mutableFetchResults;
}


+(BOOL) entityWithUIDexists: (NSString *) entityName : (NSManagedObjectContext *) managedObjectContext : (NSNumber *) uid {
    BOOL b;
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(uid == %@)", uid];
    NSMutableArray *ary=[self searchObjectsInContextCopy: entityName : managedObjectContext : predicate : nil : YES]; 
    if(ary==nil) {
        b=NO;
    } else {
     b=[ary count] >0 ? YES :NO;
    }

    [ary release];
    return b;
}
A: 

Looking at your source code, I noticed two things. First, it is wrong to initialize a fetch request without sort descriptors. Quoting from the SDK 3.1 release notes:

"NSFetchedResultsController no longer crashes when fetch request has no sort descriptors. It’s still invalid to initialize an NSFetchedResultsController without sort descriptors, but a proper exception is now raised"

Therefore, you should always initialize your NSFetchedResultsController with sort descriptors. The second thing is related to your leak. The executeFetchRequest: method return an autoreleased NSArray. You are using the mutableCopy method and you therefore are returning an object which has been retained by mutableCopy. That basically means that you are responsible for releasing the returned object. Again, quoting the mutableCopy method documentation:

"If you are using managed memory (not garbage collection), this method retains the new object before returning it. The invoker of the method, however, is responsible for releasing the returned object."

unforgiven
Thanks, adding the sort descriptor plugged the 16 byte memory leak. I believe I was already manually releasing the fetch results here:[ary release];
deanschang
see above response regarding the pragma mark. but thanks for the sort descriptor note.
deanschang
A: 

Ok, I found out something interesting. The nil sort descriptor wasn't causing the leak, it was still there but maybe I was stopping the leak detector too early. Here is the method with the leak. When I comment out the pragma mark, the 16 byte leak doesn't show in Instruments. Why would having a pragma mark in the method cause a 16 byte leak?

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSString *databaseFilePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"Locations.sqlite"];


    NSFileManager *fileManager = [NSFileManager defaultManager];

    if([fileManager fileExistsAtPath:databaseFilePath])
    {
# pragma mark - flag to delete file
     NSError *fMerror;
     if (![fileManager removeItemAtPath:databaseFilePath error:&fMerror]) {
        NSLog(@"persistentStoreCoordinator error %@, %@", fMerror, [fMerror userInfo]);    
     }

    }


    NSURL *storeUrl = [NSURL fileURLWithPath: databaseFilePath];

    NSError *error;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
        NSLog(@"persistentStoreCoordinator error %@, %@", error, [error userInfo]);    }    

    return persistentStoreCoordinator;
}
deanschang
notice there is a space in-between the # and pragma, when i remove that space, the 16 byte memory leak gets plugged. 16 byte leak with : # pragma mark - flag to delete filebut not with : #pragma mark - flag to delete file
deanschang