Core Data Overview Objc - Io
Core Data Overview Objc - Io
By Daniel Eggert
Core Data is probably one of the most misunderstood Frameworks on OS X and In this article
iOS. To help with that, well quickly go through Core Data to give you an overview of
what it is all about, as understanding Core Data concepts is essential to using What is Core Data?
Core Data the right way. Just about all frustrations with Core Data originate in
The Stack
misunderstanding what it does and how it works. Lets dive in
How the Components Play Together
Creating Objects
Saving Changes
What is Core Data? Updating Relationships
More than eight years ago, in April 2005, Apple released OS X version 10.4, which Getting to Objects
was the first to sport the Core Data framework. That was back when YouTube Changing Object Values
Core Data is a model layer technology. Core Data helps you build the model layer
that represents the state of your app. Core Data is also a persistent technology, in
that it can persist the state of the model objects to disk. But the important
takeaway is that Core Data is much more than just a framework to load and save
data. Its also about working with the data while its in memory.
If youve worked with Object-relational mapping (O/RM) before: Core Data is not an
O/RM. Its much more. If youve been working with SQL wrappers before: Core Data
is not an SQL wrapper. It does by default use SQL, but again, its a way higher level
of abstraction. If you want an O/RM or SQL wrapper, Core Data is not for you.
One of the very powerful things that Core Data provides is its object graph
management. This is one of the pieces of Core Data you need to understand and
learn in order to bring the powers of Core Data into play.
On a side note: Core Data is entirely independent from any UI-level frameworks.
Its, by design, purely a model layer framework. And on OS X it may make a lot of
sense to use it even in background daemons and the like.
The Stack
There are quite a few components to Core Data. Its a very flexible technology. For
most uses cases, the setup will be relatively simple.
When all components are tied together, we refer to them as the Core Data Stack.
There are two main parts to this stack. One part is about object graph
management, and this should be the part that you know well, and know how to
work with. The second part is about persistence, i.e. saving the state of your
model objects and retrieving the state again.
In between the two parts, in the middle of the stack, sits the Persistent Store
Coordinator (PSC), also known to friends as the central scrutinizer. It ties together
the object graph management part with the persistence part. When one of the two
needs to talk to the other, this is coordinated by the PSC.
The object graph management is where your applications model layer logic will
live. Model layer objects live inside a context. In most setups, theres one context
and all objects live in that context. Core Data supports multiple contexts, though,
for more advanced use cases. Note that contexts are distinct from one another, as
well see in a bit. The important thing to remember is that objects are tied to their
context. Each managed object knows which context its in, and each context
knows which objects it is managing.
The other part of the stack is where persistency happens, i.e. where Core Data
reads and writes from / to the file system. In just about all cases, the persistent
store coordinator (PSC) has one so-called persistent store attached to it, and this
store interacts with a SQLite database in the file system. For more advanced
setups, Core Data supports using multiple stores that are attached to the same
persistent store coordinator, and there are a few store types than just SQL to
choose from.
Our app has a single root item. Theres nothing magical to it. Its simply an item we
use to show the bottom of the item hierarchy. Its an item that well never set a
parent on.
When the app launches, we set up our stack as depicted above, with one store,
one managed object context, and a persistent store coordinator to tie the two
together.
On first launch, we dont have any items. The first thing we need to do is to create
the root item. You add managed objects by inserting them into the context.
Creating Objects
It may seem cumbersome. The way to insert objects is with the
+ (id)insertNewObjectForEntityForName:(NSString *)entityName
inManagedObjectContext:(NSManagedObjectContext *)context
+ (NSString *)entityName
{
return @Item;
}
+ (instancetype)insertNewObjectInManagedObjectContext:(NSManagedObjectContext *)moc;
{
return [NSEntityDescription insertNewObjectForEntityForName:[self entityName]
inManagedObjectContext:moc];
}
Now theres a single item in our managed object context (MOC). The context knows
about this newly inserted managed object and the managed object rootItem
knows about the context (it has a -managedObjectContext method).
Saving Changes
At this point, though, we have not touched the persistent store coordinator or the
persistent store, yet. The new model object, rootItem , is just in memory. If we
want to save the state of our model objects (in this case just that one object), we
need to save the context:
At this point, a lot is going to happen. First, the managed object context figures
out what has changed. It is the contexts responsibility to track any and all
changes you make to any managed objects inside that context. In our case, the
only change weve made thus far is inserting one object, our rootItem .
The managed object context then passes these changes on to the persistent store
coordinator and asks it to propagate the changes through to the store. The
persistent store coordinator coordinates with the store (in our case, an SQL store)
to write our inserted object into the SQL database on disk. The
NSPersistentStore class manages the actual interaction with SQLite and
generates the SQL code that needs to be executed. The persistent store
coordinators role is to simply coordinate the interaction between the store and
the context. In our case, that role is relatively simple, but complex setups can
have multiple stores and multiple contexts.
Updating Relationships
The power of Core Data is managing relationships. Lets look at the simple case of
adding our second item and making it a child item of the rootItem :
Thats it. Again, these changes are only inside the managed object context. Once
we save the context, however, the managed object context will tell the persistent
store coordinator to add that newly created object to the database file just like for
our first object. But it will also update the relationship from our second item to the
first and the other way around, from the first object to the second. Remember how
the Item entity has a parent and a children relationship. These are reverse
relationships of one another. Because we set the first item to be the parent of the
second, the second will be a child of the first. The managed object context tracks
these relationships and the persistent store coordinator and the store persist (i.e.
save) these relationships to disk.
Getting to Objects
Lets say weve been using our app for a while and have added a few sub-items to
the root item, and even sub-items to the sub-items. Then we launch our app again.
Core Data has saved the relationships of the items in the database file. The object
graph is persisted. We now need to get to our root item, so we can show the
bottom-level list of items. There are two ways for us to do that. Well look at the
simpler one first.
When we created our rootItem object, and once weve saved it, we can ask it for
its NSManagedObjectID . This is an opaque object that uniquely represents that
object. We can store this into e.g. NSUSerDefaults , like this:
Now when the app is relaunched, we can get back to the object like so:
What just happened is that the managed object context asked the persistent store
coordinator to get that particular object from the database. The root object is now
back inside the context. However, all the other items are not in memory, yet.
The rootItem has a relationship called children . But theres nothing there, yet.
We want to display the sub-items of our rootItem , and hence well call:
What happens now, is that the context notes that the relationship children from
that rootItem is a so-called fault. Core Data has marked the relationship as
something it still has to resolve. And since were accessing it at this point, the
context will now automatically coordinate with the persistent store coordinator to
bring those child items into the context.
This may sound very trivial, but theres actually a lot going on at this point. If any
of the child objects happen to already be in memory, Core Data guarantees that it
will reuse those objects. Thats what is called uniquing. Inside the context, theres
never going to be more than a single object representing a given item.
Secondly, the persistent store coordinator has its own internal cache of object
values. If the context needs a particular object (e.g. a child item), and the
persistent store coordinator already has the needed values in its cache, the object
(i.e. the item) can be added to the context without talking to the store. Thats
important, because accessing the store means running SQL code, which is much
slower than using values already in memory.
Its important to understand how data is fetched in these cases, since it affects
performance. In our particular case, it doesnt matter too much, since were not
touching a lot of data. But as soon as you do, youll need to understand what goes
on under the hood.
When you traverse a relationship (such as parent or children in our case) one
of three things can happen: (1) the object is already in the context and traversing
is basically for free. (2) The object is not in the context, but the persistent store
coordinator has its values cached, because youve recently retrieved the object
from the store. This is reasonably cheap (some locking has to occur, though). The
expensive case is (3) when the object is accessed for the first time by both the
context and the persistent store coordinator, such that is has to be retrieved by
store from the SQLite database. This last case is much more expensive than (1)
and (2).
If you know you have to fetch objects from the store (because you dont have
them), it makes a huge difference when you can limit the number of fetches by
getting multiple objects at once. In our example, we might want to fetch all child
items in one go instead of one-by-one. This can be done by crafting a special
NSFetchRequest . But we must take care to only to run a fetch request when we
need to, because a fetch request will also cause option (3) to happen; it will
always access the SQLite database. Hence, when performance matters, it makes
sense to check if objects are already around. You can use -
[NSManagedObjectContext objectRegisteredForID:] for that.
When we do this, the items title changes. But additionally, the managed object
context marks the specific managed object ( item ) as changed, such that it will be
saved through the persistent store coordinator and attached store when we call -
save: on the context. One of the key responsibilities of the context is change
tracking.
The context knows which objects have been inserted, changed, and deleted since
the last save. You can get to those with the -insertedObjects , -
updatedObjects , and -deletedObjects methods. Likewise, you can ask a
managed object which of its values have changed by using the -changedValues
method. You will probably never have to. But this is what Core Data uses to be able
to push changes you make to the backing database.
When we inserted new Item objects above, this is how Core Data knew it had to
push those to the store. And now, when we changed the title , the same thing
happened.
Saving values needs to coordinate with both the persistent store coordinator and
the persistent store, which, in turn, accesses the SQLite database. As when
retrieving objects and values, accessing the store and database is relatively
expensive when compared to simply operating on objects in memory. Theres a
fixed cost for a save, regardless of how many changes youre saving. And theres a
per-change cost. This is simply how SQLite works. When youre changing a lot of
things, you should therefore try to batch changes into reasonably sized batches. If
you save for each change, youd pay a high price, because you have to save very
often. If you save too rarely, youd have a huge batch of changes that SQLite would
have to process.
It is also important to note that saves are atomic. Theyre transactional. Either all
changes will be committed to the store / SQLite database or none of the changes
will be saved. This is important to keep in mind when implementing custom
NSIncrementalStore subclasses. You have to either guarantee that a save will
never fail (e.g. due to conflicts), or your store subclass has to revert all changes
when the save fails. Otherwise, the object graph in memory will end up being
inconsistent with the one in the store.
Saves will normally never fail if you use a simple setup. But Core Data allows
multiple contexts per persistent
BOOKS 3 store
ISSUES 24coordinator, so you can run into conflicts at Blog Newsletter About Search
the persistent store coordinator level. Changes are per-context, and another
context may have introduced conflicting changes. And Core Data even allows for
completely separate stacks both accessing the same SQLite database file on disk.
That can obviously also lead to conflicts (i.e. one context trying to update a value
on an object that was deleted by another context). Another reason why a save can
fail is validation. Core Data supports complex validation policies for objects. Its
an advanced topic. A simple validation rule could be that the title of an Item
must not be longer than 300 characters. But Core Data also supports complex
validation policies across properties.
Final Words
If Core Data seems daunting, thats most likely because its flexibility allows you to
use it in very complex ways. As always: try to keep things as simple as possible. It
will make development easier and save you and your user from trouble. Only use
the more complex things such as background contexts if youre certain they will
actually help.
When youre using a simple Core Data stack, and you use managed objects the
way weve tried to outline in this issue, youll quickly learn to appreciate what Core
Data can do for you, and how it speeds up your development cycle.
Core Data
September 2013