Simple memory management with core data and ARC - iphone

I am still getting used to some of the details of memory management in objective-c, despite the fact that ARC simplifies things significantly. Here is a line of code:
[song addEntriesObject:self.entry];
where song is a managed object that I access by executing a fetch request on my managed object context, and self.entry refers to an instance variable that is passed into this class upon initialization, and is also a managed object (i.e. it's a core data entity - hope I'm using that terminology properly).
My question is simply whether this will cause any problems in memory management, i.e. whether assigning self.entry to a managed object will prevent that instance variable from being released because it is being retained by core data, or something along those lines. Would this create any problems, or does core data not maintain pointers to objects in the same way? Or am I just completely on the wrong track with my thought process here?
Any thoughts on the subject would be much appreciated.

cowfaboo,
Core Data objects are NSObjects and hence behave identically. In other words, nothing changes with respect to memory management. In your example, self.entry is retained by both your instance and by song.
Independent of ARC, Core Data items can retain large object graphs. These may need to be pruned. The -refreshObject:mergeChanges: method is used to do this. To save yourself some pain, you should always use -save: before trimming your graph.
Andrew

Related

How to deal with Core Data retain cycles

The core data guidelines recommend that you model your relationships with an inverse. No problems there.
Interestingly though if you Load an object A that has a to many relationship to B and walk the object graph you end up with a retain cycle and the memory is never freed.
For a simple object graph you can just call refreshObject:mergeChanges: on A to re-fault the object so that relationships are no longer strong references.
If you have a complicated object graph though this is a pain because you need to call it on every object you have touched. It seems like a pretty important consideration when using core data yet there is only one paragraph on this topic in Apples documentation.
I am just wondering how other people handle this? A long running app would slowly just consume more and more memory without some sort of manual process to force objects to revert to faults.
Are there any known patterns for dealing with this. I'd imagine so since lots of people use Core Data I just can't find any recommendations
You are ignoring several aspects of core data when making your assertions. If you fetch an object, let's say object A, which has a one-to-many relationship to object B, when you fetch A, you will have all the objects on B which are related to A. A one to many relationship creates the list of objects related to A and contains them on an NSSet property of your NSManagedObject subclass. Note that these objects are in a faulted state, and the memory footprint from this is insignificant. If you manipulate the objects in the relationship, core data will unfault these objects when necessary. You do not have to do anything to get this behavior. If you want to trigger the faulting behavior yourself to send the objects to fault again, you can use refreshObject:mergeChanges:. If you do not send them back to fault, the faulting behavior will be trigger again eventually.

Does Core Data take appropriate action automatically when there's a Low Memory Warning?

Does it turn some managed objects into faults when there's a Low Memory Warning? Or must we do that manually by calling the -refreshObjects:mergeChanges: method which puts the affected managed objects on diet quickly? And...would that actually hurt? What if these objects are currently used by an NSFetchedResultsController to show up on a table view?
Yes, Core Data listens for low memory warnings and will drop its cache and attempt to fault any object that it can to reduce its memory consumption.
If the object is currently being used it would automatically realize that object the next time you accessed on of its properties so from your application's point of view, nothing has happened and no NSManagedObject entities have been released.
In the case where you have established a relationship between two objects, you MUST use the managedObjectContext method -refreshObject:mergeChanges:NO after doing a save, to tell CoreData that the related object can be released. I think of it as the moral equivalent to a [object release]; statement; you are telling CoreData to release the object it read it from the DB. The tradeoff, as Marcus says, is that it will be released from memory cache, but have to be read back in by core data. the good news is that it happens behind the scenes and you don't have to program for it; the bad news is that it happens behind the scenes and thus can 'magically' chew up memory.
Ways around it are many; for example, use a predicate to only select the rows you absolutely must need; don't do a general call to fetch everything and then go through the list one by one. More than likely you will crash when you do the general call and CoreData attempts to load all objects.

What's the staleness interval good for?

When an managed object is fetched, and the staleness interval is set to 5 minutes, what happens after 10 minutes, when I access an property of that object?
Would Core Data then perform a fresh fetch request? Does that make sense? I can't think of a situation where data is already cached but the object is a fault. When can this happen?
From Core Data Programming Guide: Using Managed Objects
Note that an object's staleness
interval is the time that has to pass
until the store re-fetches the
snapshot. This therefore only affects
firing faults—moreover it is only
relevant for SQLite stores (the other
stores never re-fetch because the
entire data set is kept in memory).
It only affects full fledged objects -- it does not affect those that are faults (ie "ghost objects" with no populated attributes.)
You really only need to fiddle with this in a complex setup where you have multiple context all changing the store simultaneously. On iOS apps, this is rarely necessary.
If the object in question is not a fault, but a fully realized object (it's properties have been populated), then nothing will happen if you access a property of this object in 10 minutes.
stalenessInterval affects only objects that are faults. If you have such an object and you initiate fulfilling a fault by, for example, accessing a property, then Core Data can either get property values from its internal cache or from a persistent store. If time in stalenessInterval since last fetch from the store has elapsed, then property values will be fetched from the store, otherwise–from the cache, which is much faster.
Yes, there can be a situation when the data is already cached, and the object is a fault. You can do this yourself by trimming the object graph for breaking relationship strong reference cycles.
When you have relationships between managed objects, each object maintains a strong reference to the object or objects to which it is related. This can cause strong reference cycles. To ensure that reference cycles are broken, when you're finished with an object you can use the managed object context method refreshObject:mergeChanges: to turn it into a fault.

What's the point of setRetainsRegisteredObjects:?

Why would I want to set that to YES? What difference would that make? Must I worry about this?
setRetainsRegisteredObjects: to YES makes your context maintain a strong reference to managed objects that it would otherwise maintain a weak relationship with. When you perform a fetch request, the objects returned have a weak reference (by default) to the respective managed object context. Only when an object is modified (added, changed, deleted) does the managed object context (MOC) maintain a strong relationship to the object.
Setting setRetainsRegisteredObjects: to YES ensures that strong pointers will be maintained between all fetched objects.
I don't know what #TechZen is talking about - this can be the cause for a sneaky bug if you're not careful. It's a useful method to invoke on the MOC when you find yourself in a situation where this would be useful.
Worry? I don't know, are you interested in wasting time?
You only fiddle with this particular context attribute when you want to do custom memory management within Core Data (which you almost never do.) I had to go look this up just to remember what it was because I haven't used it in years.
The rule of thumb with Core Data is that if you have an attribute with a default value then you use the default value in the vast majority of cases. That's why its the default.
Unless you see a context attribute changed in virtually every example i.e. the store name, then it is not necessary to change it in 90% of the uses. It is certainly not necessary for a novice to try and change it.
Core Data is intended to be relatively simple once you under it abstractly. Using binding, it is possible to use Core Data on the Mac without writing any code at all. Everything just works with the default configuration.

What's the reason why core data takes care of the life-cycle of modeled properties?

The docs say that I should not release any modeled property in -dealloc. For me, that feels like violating the big memory management rules. I see a big retain in the header and no -release, because Core Data seems to do it at any other time.
Is it because Core Data may drop the value of a property dynamically, at any time when needed? And what's Core Data doing when dropping an managed object? If there's no -dealloc, then how and when are the properties getting freed up?
Core Data has NSManagedObjects as their base object, they are managed by the system and you do not have to do any memory managment on them...well unless u declare your own properties in a class that are not defined in the object model...
The tricky bit of managed object memory comes when the objects are turned into faults. A fault is a ghost of the object. It exists and responds to messages but it doesn't have it's attributes populated. For example, you can count the number of objects in a to-many relationship even if the objects themselves are faults.
Faults are used to maintain the object graph in memory without the overhead of the actual data. The object is still technically alive so its dealloc has never been called.
When the context turns an object into a fault, the object releases and nils it's attributes in memory. It does this in willTurnIntoFault and didTurnIntoFault. If you need to do some special releasing you should override those methods (usually the latter.)
Faulting makes managed objects rely on different methods than other classes for intialization and clean up. You intialize in awakeFromInsert or awakeFromFetch and you dealloc in willTurnIntoFault and didTurnIntoFault.
It's important to follow the rules for managed objects because the context might keep thousands of faulted objects in memory. If you create a custom attribute but do not release it when the object becomes a fault, then you could easily tie up a large amount of memory accidentally.