1

I want to insert 200 5Mb records in my Core Database. But when I save the NSManagedObject, the memory wasn't released (autoreleased pool didn't help), and after inserting 30 records I got the memory warning and the application crashed. Here is my code

- (void)SaveItem
    {
        NSString *entityName = kEntityName;
        AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context = appDelegate.managedObjectContext;
        NSEntityDescription *entityDesctiption = [NSEntityDescription 
                                                  entityForName: entityName
                                                  inManagedObjectContext:context];
        // check if town exists
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id == %d", self.imageID];
        NSFetchRequest *requestToCheckExistense = [[NSFetchRequest alloc] init];
        [requestToCheckExistense setEntity:entityDesctiption];
        [requestToCheckExistense setPredicate:predicate];
        NSArray *objects = [context executeFetchRequest:requestToCheckExistense error:nil];
        [requestToCheckExistense release];
        if (objects == nil)
        {
            NSLog(@"there was an error");
        }
        NSManagedObject *object;
        if ([objects count] > 0)
        {
            // edit item
            object = [objects objectAtIndex:0];
        }
        else
        {
            // if object doesn't exist, find max id to imlement autoincrement
            NSFetchRequest *request = [[NSFetchRequest alloc] init];
            [request setEntity:entityDesctiption];
            request.propertiesToFetch = [NSArray arrayWithObjects: @"id", nil];
            NSArray *allobjects = [context executeFetchRequest:request error:nil];
            [request release];
            NSInteger newID = 1;
            if ([allobjects count] > 0)
            {
                NSNumber *maxID = [allobjects valueForKeyPath:@"@max.id"];
                newID = [maxID intValue] + 1;
            }

            // write item
            object = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context]; 
            [object setValue:[NSNumber numberWithInt:newID] forKey:@"id"];
            self.imageID = newID;
        }
        // fill NSManagedObject
        // size of objNSData is about 5MB
        NSMutableData *objNSData = [[DatabaseManager sharedDatabaseManager] encryptedDataFromImage:bigImage];
        [object setValue:objNSData forKey:@"big"];

         [context save:nil];
     }

When I commented

[object setValue:objNSData forKey:@"big"];

everything was OK.

I tried to add the code into @autoreleasepool , but that didn't help. I know, that now, when I save data to database, it's still in iPhone RAM. How to release it from this memory? When I get a set of Managed Objects from the database, they are not in the RAM (I can easyly get 100 object, each of them has 5Mb fields)

Paul T.
  • 4,938
  • 7
  • 45
  • 93

3 Answers3

2
 object =(tblEntity *) [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];


try to type cast the object,this may solve the problem

Bad Boy
  • 628
  • 2
  • 5
  • 23
2

I've solved the issue. after call of [self SaveItem]; I used

[context save];
[context reset];
[context save];

all the NSManagedObjects from the context will be released. After that operation I can add as many big objects as I want

Paul T.
  • 4,938
  • 7
  • 45
  • 93
1

Because you don't own an NSManagedObject when you create it, it may be retained by the core data stack even after releasing it (when using an autoreleasepool contained inside the loop).

This may help:

  • Set the undo manager of your managedobjectContext to nil:

    [context setUndoManager:nil];

  • Be sure that no properties of that object are retained anywhere, because then the managed object will not be released on time inside your loop.

  • Be sure to add an autorelease pool inside every loop execution, not wrapping all the loop itself, similar to:

    for(i;i<n;i++) {
        NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
        [obj saveItem];
        [pool drain];
     }
    
  • If that object belongs to a hierarchy of NSManagedObjects, then you need to release the owner of this object too, for this one to be deallocated from memory.

You can check apple's documentation about memory management in CoreData.

Warning: big objects (> 1MB) are not recommended by Apple to be stored inside CoreData (Check this other question/answer.)

Community
  • 1
  • 1
DarthMike
  • 3,471
  • 1
  • 22
  • 18
  • 1) I tried [context setUndoManager:nil]. Didn't help. 2) the objects are not retained anywhere. 3) Tried to use you approach of autorelease pool. Didn't help. It seems, that NSManagedObjectContext retains these NSManagedObjects – Paul T. Jun 05 '12 at 13:05
  • You can try and sleep between every adding after a save operation, then you may see if the objects are released after some time. Also, another idea I have is to create a new context for every save. Did you try it? – DarthMike Jun 05 '12 at 13:27