deanschang
deanschang

Reputation: 1

coredata memory leak

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;
}

Upvotes: 0

Views: 2378

Answers (2)

deanschang
deanschang

Reputation: 1

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;
}

Upvotes: 0

Massimo Cafaro
Massimo Cafaro

Reputation: 25429

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."

Upvotes: 1

Related Questions