How to deal with Core Data retain cycles - iphone

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.

Related

Does fetching object also fetches relationship objects in core data

I have one to many relationship b/w objects in core data.
Like A==>>B(many objects)
So A is has one to many relationship with B objects.
My Question is that when I fetch A object then does B objects also gets loaded into memory?
If No, then when they gets loaded? When I access relationships?
In most cases it doesn't. This is a mechanism called Faulting in Core Data. The framework takes care of realizing faults behind the scene when you ask for an object that has not yet been realized (i.e. loaded in memory).
Fault handling is transparent—you do not have to execute a fetch to realize a fault. If at some stage a persistent property of a fault object is accessed, then Core Data automatically retrieves the data for the object and initializes the object (see NSManagedObject Class Reference for a list of methods that do not cause faults to fire). This process is commonly referred to as firing the fault. If you send the Department object a message to get, say, its name, then the fault fires—and in this situation Core Data executes a fetch for you to retrieve all the object's attributes.
So in your example, if you load A, Core Data will fault the B instances (i.e. not load them into memory) and when you actually try to access B, then it realizes the fault (i.e. loads into memory).
Conversely, there are times when you have loaded objects in memory and you would like to "unload" them. This is called turning objects into faults.
You can turn a realized object into a fault with the refreshObject:mergeChanges: method. If you pass NO as the mergeChanges argument, you must be sure that there are no changes to that object’s relationships. If there are, and you then save the context, you will introduce referential integrity problems to the persistent store.

CoreData: efficient way to fetch and relate entities

I have a CoreData database full of objects that relate to each other.
EntityFoo - attribute: uniqueId, relationship: one-to-many to other EntityFoo objects, relationship: one-to-many to other EntityUnresolvedRelationship objects
EntityUnresolvedRelationship - attribute: uniqueId, inverseRelationship: back to EntityFoo object
When my iPhone application starts, I get the information for these objects from a web service. I parse the web service response and create the Foo entities. At the time I download and parse the data and put the Foo objects into CoreData I don't want to take the time to find the other EntityFoo objects that need to be related to this object, and it is likely that I do not yet have a Foo object to relate yet if it has not yet been downloaded and parsed, so I quickly make an UnresolvedRelationship object for the relationship and store the uniqueId in it so that I can resolve the relationship for this object later.
Now I am trying to figure out the most efficient way to walk through all of the Foo or UnresolvedRelationship objects and create the proper CoreData relationships between all of the Foo objects. In other words, in the end I want to have no UnresolvedRelationship objects...this was just temporary...the Foo objects will only have relationships to each other.
There could be 15,000 plus Foo objects.
Is there a good way to fetch all of the Foo objects and all of the UnresolvedRelationship objects in a way that I can walk one of the arrays and quickly find the matching entity in the other array by it's uniqueId so that I can setup the CoreData relationship?
Anyway, would love any pointers.
you should be doing this as part of the parsing of the data coming from the web service. That is going to be the most efficient way to do it. The way you are approaching this is that you are guessing that the relationship lookup is going to be slow. That is a bad approach to optimization.
Fetching the objects and linking them is not going to be slow unless you cause them to be realized (faulted) into memory. If you are faulting 15K objects, it is going to be slow anyway.
Set up you relationships while parsing, use autorelease pools effectively during your parsing and reset your context at consistent intervals during the parsing to keep memory down. That is the best option.

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