Reputation: 6015
I am inserting thousands of data into my SQLite stored Core data . My ManagedObjectContext's undomanager set to nil. I am inserting NSManagedObject in a for loop. tried saving the context in two way,
1 after completion of loop (outside the for loop).
for(int i =0;i<1000;i++){
MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];
object.name = [NSString stringWithFormat:@"%d",i];
object.age = [NSNumber numberWithInt:i];
}
[self managedObjectContext] save:nil];
2 for every insertion (inside the for loop).
for(int i =0;i<1000;i++){
MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];
object.name = [NSString stringWithFormat:@"%d",i];
object.age = [NSNumber numberWithInt:i];
[self managedObjectContext] save:nil];
}
and finally i called reset for ManagedObjectContext .
[[self managedObjectContext] reset];
but the problem is memory taken for this process(insertion) is not get back after the completion.
Can any one suggest me whats is wrong with this, or this is the expected behavior ? Any workaround for avoiding this memory leak?
UPDATE
I tried [[self managedObjectContext] refreshObject:object mergeChanges:NO];
It can put back some amount of memory, but not fully.Still searching a good solution for managing large amount of data in CoreData
.
If any one suggest me an example project(source code) which handle large amount of data in CoreData
,then will be helpful for me .
Thank You.
Upvotes: 3
Views: 2243
Reputation: 57
We had the same problem in our project. Reseting the context barely freed any memory. Turns out the problem was fixed when reset was done on all parent contexts:
NSManagedObjectContext *currentContext = context;
while (currentContext) {
[currentContext reset];
currentContext = [currentContext parentContext];
}
Upvotes: 2
Reputation: 3738
while this is a bit late,
i found that setting undo
to nil
did not help, but the following did prevent leaks in core data.
[[self managedObjectContext] processPendingChanges]; // flush operations
[[[self managedObjectContext] undoManager] disableUndoRegistration]; // disable undo
regarding autoreleasepool. hitting an exception (including as Numberformat exception) object.name = [NSString stringWithFormat:@"%d", i];
your pool will not drain as desired. in fact, another autoreleasepool will drain all autoreleased instances. memory will leak, such as the exception which is created via autorelease. so the following is suggested.
NSException *myexception = nil;
try {
.... [yadda yadda];
} catch (NSException e)
{
myexception = [e retain];
@throw;
}
finally {
[pool drain];
[myexception release];
}
i would suggest not putting in the autoreleasepool, sort out whether [[[self managedObjectContext] undoManager] disableUndoRegistration];
helps on a smaller set and then move larger. undo manager in core data is a leaky boat, and i have not figured out a solution.
Upvotes: 1
Reputation: 6267
Your code lacks autoreleasepools :D this means you're storing 1000 MyEntity objects in memory, and this WILL lead to crashes and iOS will FC your app (force close). The code should look like:
for(int i = 0; i < 1000; i++) {
@autoreleasepool {
MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];
object.name = [NSString stringWithFormat:@"%d", i];
object.age = [NSNumber numberWithInt:i];
}
}
[self managedObjectContext] save:nil];
OR:
for(int i = 0; i < 1000; i++) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];
object.name = [NSString stringWithFormat:@"%d", i];
object.age = [NSNumber numberWithInt:i];
[pool drain];
}
[self managedObjectContext] save:nil];
Also, this may not be the best way to do this kind of thing, I would suggest adding it to the context, then operating on the context itself perhaps using
[[NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:managedObjectContext] name] = [NSString stringWithFormat:@"%d", i];
or something of the sort. This way you don't create a local copy of the object and clog up your memory.
Using the last of three methods, I would do it like this:
for(int i = 0; i < 1000; i++) {
[NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];
[[NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:managedObjectContext] name] = [NSString stringWithFormat:@"%d", i];
[[NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:managedObjectContext] age] = [NSNumber numberWithInt:i];
}
[self managedObjectContext] save:nil];
And if this MyEntity is your own custom class, you can even simplify your code by using
@property(nonatomic, retain) id name;
@property(nonatomic, retain) id age;
Replace id name
and id age
with the correct variable and object class, and this way your code will be even simpler:
for(int i = 0; i < 1000; i++) {
[NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];
[[NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:managedObjectContext] setName: [NSString stringWithFormat:@"%d", i]];
[[NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:managedObjectContext] setAge: [NSNumber numberWithInt:i]];
}
[self managedObjectContext] save:nil];
Hope this helps and doesn't confuse you!
Upvotes: 1
Reputation: 1202
This is subject of autorelease pool issues. Without authorelease application will keep memory some time (only apple guys know, how much time. If you like to take process in your hands, just change you code:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(int i =0;i<1000;i++){
MyEntity *object = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];
object.name = [NSString stringWithFormat:@"%d",i];
object.age = [NSNumber numberWithInt:i];
[self managedObjectContext] save:nil];
[pool drain], pool = nil;
pool = [[NSAutoreleasePool alloc] init];
}
[pool drain], pool = nil;
Upvotes: 1