views:

61

answers:

3

I am having a lot of trouble getting m core data objects to be deallocated. I even tried the simplest test I could think of and I watched the retain count and it always seems to be higher than I expect. I also put a breakpoint on the dealloc routine for the core data objects and it doesn't seem to get called (at least not unless I specifically add additional releases).

I definitely have [managedObjectContext setRetainsRegisteredObjects:NO] by the way, so that isn't the problem.

Here is an example I've tried that doesn't work:

MobileObject *obj = [NSEntityDescription insertNewObjectForEntityForName:@"MobileObject" inManagedObjectContext:managedObjectContext];

NSLog(@"Object count after creation %d", [obj retainCount]);

NSError *error;
[managedObjectContext save:&error];

NSLog(@"Object count after saving %d", [obj retainCount]);

Each log message prints out "2". Dealloc never gets called. I'd expect the save to cause the object to be released (or autoreleased) since core data should realize it doesn't need to stay around. In that case the retain count of 2 makes sense if there are two autoreleases (one from the creation and one from the save) but that doesn't seem to be the case. If I add

[obj release] 

at the very end of my test, the dealloc routine will get called correctly but I am very confused as to why I need this release at all.

+4  A: 

Saving a CoreData context is not logically related to releasing an NSManagedObject.

In a big program, it is quite common to have the managed object context (moc) all the while your program is running. Objects are added to and removed from the context reflecting the user's action, and the context is also saved once in a while so that the context in memory is synced to what you have on the file. But in this case, saving the context does not mean you're done with the context; you still keep the context and use it just as before the save. So, saving doesn't mean deallocating the objects.

So, if you want to deallocate objects you retain, you need to explicitly release them.

However in this case, insertNewObjectForEntityName:inManagedObjectContext: returns an autoreleased object as written here. So you do not own the object unless you retain it explicitly. If you didn't retain, you shouldn't release it. It's released and dealloc is called automatically when appropriate.

Note that you should never rely on retainCount to see what is going on. For a simplistic object, the number of retainCount would agree with what we expect, but for something complicated like an NSManagedObject, the framework itself needs to keep track of a lot of things, and so might do a lot of retain behind the scenes. For example, even if you do not explicitly retain an object, it might be in a relationship of another object which you own; or CoreData might just decide to keep the object in the memory to cache it.

The only thing to keep in mind is to stick to the standard ownership rule. If you alloc/init or retain, you later release or autorelease it.

Answering your question in the comment, let me state the basic principle here: If an object x needs another object a, x retains a. When x finishes using a, x releases a. Every object x should follow this procedure, whether x is an object which you code, or which Apple's framework provides. If every object follows this principle, there's no leak. So, Apple's code which you don't see follows this. Your code should follow this, too.

This answers your question:

when I "add" a child object to a parent does the parent object automatically retain the object I added?

Yes it does. The parent needs it, so it retains it.

And if I then release the parent object will it release the child or to I have to do that explicitly?

Yes the parent does release the child, because when the parent is dealloced, it finishes using the child, so the child is releaseed.

These points are not usually written in the documentation of the individual methods, because as the rule, every API follows this basic memory management principle, and therefore there's no need to spell them out.

You're encouraged to read Memory Manegement Rules.

Yuji
It seems like it will be impossible to tell if my program is ever correct then. For example, I have some complicated relationships between my objects and when I "add" a child object to a parent does the parent object automatically retain the object I added? And if I then release the parent object will it release the child or to I have to do that explicitly? I'm very worried about accidentally holding on to objects that I don't need anymore. I'm amazed that anyone can write an Objective C application that does not have at least one memory leak.
Mike
@Mike -- All you have to do to manage memory is learn to pair the initialization of an object with a release. Since each class manages its own memory as a module, you can have an infinite number of nested objects all easily managing memory. The trick lays in learning to distinguish between an initialization and a reference. You have to learn to distinguish methods that create a new object inside the owning object and ones that simply return the address of an object created elsewhere.
TechZen
added an answer to your further questions.
Yuji
A: 

When you are working with managed objects in Core Data you normally don't use retain or release as Core Data takes care of those kinds of things for you. If you have an object you know you don't need to use for a while you can re-fault it (this will strip it down to a skeleton that takes less memory. To re-fault an NSManagedObject make sure any changes to it have been saved and that any changes from other contexts (if you have any) have been merged, then just call [managedObjectContext refreshObject:objectToFault mergeChanges:NO];

theMikeSwan
+1  A: 

Core Data will manage its own memory, you do not need to worry about it. Just follow the rules of retain/release and let Core Data take care of itself.

When you are testing for leaks, that is when you need to make sure that your code is following the rules.

Core Data can and will keep objects around long after you are done with them. It will release them only when it has a low memory situation.

If you want to force memory to drop (such as when you go into the background on iOS4) them you should call -reset on your NSManagedObjectContext and that will force Core Data to purge as much memory as possible. But even then there is no guarantee that a NSManagedObject will be dealloc'ed. It will, however, be faulted.

Short answer ... don't worry about it.

Marcus S. Zarra