i would like to understand a bit more Core Data, why do we "fetch" and search for entities while the entities are "inside" managed objects? For example :
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription =
[NSEntityDescription entityForName:#"Employee" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
also, what does a persistent object store contain? if i understood, the persistent object store takes data from a sqlite file, but then it gets a bit confused, is it : one entity, for one persistent object store, for one data inside the sqlite file?
Thanks for your answers
Paul
Basically there are 5 components here. A persistent store coordinator, a managed object context, a managed object model, entities and managed objects. They all work together to provide an object graph management system (note that Core Data is not an ORM so it helps not to think of it that way). Below is a description of the components and the various other classes in CoreData that interact with them
NSPersistentStoreCoordinator - This handles the loading of data to and from disk. It deals with various stores (NSPersistentStore). The included store types are binary, XML and SQLite. You can write your own stores (using the NSAtomicStore and NSIncrementalStore classes), for example if you have your own file type (theoretically you could write a store to open a Word or Photoshop file if you desired)
NSEntityDescription - An entity can be sort of thought of as the "class" of a managed object. It defines any attributes (NSAttributeDescription), relationships (NSRelationshipDescription) and fetched properties (NSFetchedPropertyDescription) that a managed object should have, as well as other properties such as the NSManagedObject subclass that should be used
NSManagedObjectContext - This is the in memory "scratch pad". It is where you query for objects (using NSFetchRequests), create objects, delete objects etc. You can have multiple contexts, and throw one away without saving to discard any changes you no longer need.
NSManagedObject - The core unit of Core Data. These are your model objects that hold your data. You set the attributes, relationships etc on them.
NSManagedObjectModel - This represent the data model to use for your data, which is usually defined in a .mom file created within Xcode. This is where all the entities are stored.
That's pretty much the whole of core data. There are some other classes for doing migrations and merging
Related
Scenario:
I am fetching different entity objects from my Core Data database and collecting them all into a single NSMutableArray. I have no problem massaging the data in the array and saving back to the Database. I need to save the state of the array between application launches.
Question:
What is the best way to save a description of the entity objects in the array as well as the order they are in the array? (I have already tried saving the array to a dictionary, encoding the entity objects in to NSData. Didn't work because I couldn't decode the entity object.)
What is the best way to restore the array and fetch the entity objects from the database?
Thanks guys.
You have a couple different options. If the order of the objects in the "array" does not change too much, you can add an entity "MyFavoriteEntities" or some such, and give it a to-many ordered relationship to the entities you want to be included.
Then, all you do is load that single MyFavoriteEntities object, and access all the other objects through that relationship.
NOTE: Currently, ordered relationships are not supported on iCloud.
The alternative (and in lots of situations, the preferred method) is to add an "ordering" attribute to your entities. Keep that updated with the order of your objects. Then, you can easily fetch the objects by giving a SortDescriptor for the "ordering" field. now, your objects will be fetched in the order you want.
I'm not sure how to maintain a bi-directional relationship between my core data entities and some objects that are instantiated when the entities are created and committed to the database.
I have many subclassed MKAnnotation objects with one-to-one relationships to the entities. Every time my fetchedResultsController executes a new fetch, I am assuming that the results from a previous fetch are released and the NSManagedObjects that are fetched are remapped in memory. So my one-to-one relationships are broken. If I can save a pointer to the MKAnnotation objects in core data, that would fix half of the problem (the relationship in one direction). Does this make sense? How would you do this?
I delete all of the core data content when the application is restarted, so long term persistence of the relationship information is not a concern that I have.
Mixing pointers and managed objects is usually futile because Core Data has so many optimizations in place that direct memory management is all but impossible e.g. an object may revert to a fault.
You're really going about this the wrong way. Core Data isn't primarily a persistence API, its a data modeling API intended to provide the complete model layer of a Mode-View-Controller design app. As such, you can use it without saving anything at all. If you are using Core Data and you have data such as map annotation, the annotation should be modeled in Core Data. Doing so will simplify everything.
Since there is no MSAnnotation class but merely a MKAnnotation protocol, the simplest solution in this case would be to create a NSManagedObject class that implements the MKAnnotation protocol. You can either convert location data like CLLocationCoordinate2D into NSValues or better yet, just make attributes for them. Since the class implements the protocol, you could pass the managed objects anywhere you would pass any protocol object.
I am implementing "duplicate" functionality in my iOS application. I'm using the following workflow:
present a list of managed objects in initial context in root view controller
when user taps on a row, create a new context pass it to "detail" view controller with duplicated managed object ([[DetailController alloc] initWithObject:clonedObject inContext:newContext]).
However I am struggling with the concept of reassigning relations from source object to the cloned one since their managed object contexts differ. What would be correct approach to this:
Should I just reassign the pointer value and do not bother about MOC or...
I should refetch the values in new context depending on their unique identifiers?
Any other option I did not think of?
P.S. Contexts are using same persistent store coordinator.
Managed object IDs are thread safe. As such, you can pass a managed object ID to the MOC in your view controller, retrieve that object via existingObjectWithID:error, then perform the duplication in that context. This way, the objects never cross MOC boundaries.
I've once again read through the Apple developer Core Data documentation and found it lacking when it comes to the graphical Xcode 4 editor when creating SQLLite entities much as I found it lacking when IB was separate in Xcode 3.
Three tables:
ZipData
LocationData
CrossReference
CrossReference has the primary key of ZipData and LocationData so I only need to query CrossReference to get all zips for locations or all locations for zips. This means of course a to-many relationship on both ZipData and LocationData (and perhaps on CrossReference?).
What I have (that isn't working) relationship-wise is this :
ZipData has a relationship "locations" that points to LocationData and is inverse
LocationData has a relationship "zipsCodes" that points to ZipData and is inverse
CrossReference table has two relationships, one to ZipData (and is inverse) and one to LocationData (and is inverse).
I'm not sub-classing any of the entities as NSManagedObjects just yet. I am simply doing the code below in the viewDidLoad method, just to see if what I have setup works.
// test/learn the core data frame work
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *locationData = [NSEntityDescription
insertNewObjectForEntityForName:#"LocationData"
inManagedObjectContext:context];
[locationData setValue:#"Testville" forKey:#"City"];
[locationData setValue:#"United Tests" forKey:#"Country"];
[locationData setValue:#"County of Test" forKey:#"County"];
NSManagedObject *zipCodeData = [NSEntityDescription
insertNewObjectForEntityForName:#"ZipCodeData"
inManagedObjectContext:context];
[zipCodeData setValue:[NSNumber numberWithDouble:1111.00] forKey:#"Income"];
[zipCodeData setValue:[NSNumber numberWithDouble:22.00] forKey:#"LandArea"];
[zipCodeData setValue:#"23060" forKey:#"ZipCode"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"CrossReference" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
NSLog(#"LocationId: %#", [info valueForKey:#"LocationDataId"]);
NSManagedObject *details = [info valueForKey:#"details"];
NSLog(#"ZipId: %#", [details valueForKey:#"ZipCodeDataId"]);
}
[fetchRequest release];
I don't understand how to setup these relationships and not sure how to trust that somehow the primary keys are setup and the entities just kind of find their way together.
I'm getting nothing back in the logs even though when I view the simulators sqllite db I see the test entities have been persisted (but nothing in CrossReference). I know I'm missing something relationship wise but I can't put my finger on it.
Your major problem is revealed by the bolded phrases below:
I've once again read through the Apple
developer Core Data documentation and
found it lacking when it comes to the
graphical Xcode 4 editor when creating
SQLLite entities much as I found it
lacking when IB was separate in Xcode
3.
Three tables:
ZipData LocationData CrossReference
CrossReference has the primary key of
ZipData and LocationData so I only
need to query CrossReference to get
all zips for locations or all
locations for zips.
There is no such thing as SQLite entities and Core Data does not have tables or primary keys.
Core Data is not SQL. Entities are not tables. Objects are not rows. Attributes are not columns. Relationships are not joins. Core Data is an object graph management system that may or may not persist the object graph and may or may not use SQL far behind the scenes to do so. Trying to think of Core Data in SQL terms will cause you to completely misunderstand Core Data and result in much grief and wasted time.
You are making a mistake common to people skilled in SQL. You are assuming that Core Data is a lightweight object wrapper around procedural SQL. It is not. The SQLite store is but one of four persistence options and the data model itself is wholly independent of which persistence option you choose i.e. the same model will work with all types of stores. The stores are just different means of archiving and de-archiving (freeze drying and rehydrating) a graph of live objects. How a specific graph gets persisted is a behind the scenes implementation detail you can ignore in the vast majority of cases. Simply forget everything you know about SQL because it won't help you understand Core Data.
Your specific problem here is that you never set the relationships between the objects. You need to create a CrossReference object and set it's relationships, thusly;
NSManagedObject *crossReference = [NSEntityDescription
insertNewObjectForEntityForName:#"CrossReference"
inManagedObjectContext:context];
[crossReference setValue:locationData forKey:#"location"];
[crossReference setValue:zipCodeData forKey:#"zipCode"];
The context will ensure that the reciprocal relationship is set on the related LocationData and ZipData objects.
The key to mastering Core Data is to ignore the form of persistence and to instead think only in terms of objects with attributes and relationships. Once you really internalize that concept, then every thing falls into place easily.
I have a deployed app that samples measurements from sensors (e.g., Temp °C, Pressure kPa). The user can create Experiments and collect samples. Each sample is stored as a Run, such that there is a one-to-many relationship from Experiment to Run. In the interest of performance, Run has a to-one relationship with Data entity (which is where the actual raw data is stored); this allows some Run attributes to be loaded without necessarily loading lots of data.
Most of our sensors have multiple measurements, so it would be nice to store all the data that is actually being sampled. But this means that the Run <---> Data relationship needs to become Run <-->> Data (to use Xcode's convention).
I am faced with trying to migrate data from old Run to-one Data model to new Run to-many Data model. Can this be done using Mapping Models? If so, does anyone have any pointers to examples? If not, does anyone have any pointers to examples of how to do that?
Thanks for any pointers or advice.
That migration should be easy enough that automatic migration will work. Worst case is that it would require a mapping model but I suspect it will "just work" by turning on auto migration.
I ended up needing to subclass NSEntityMigrationPolicy several times. This was necessary because properties were moving from/to different entities with several levels of abstraction being added to support a considerably more general model. Also important was the ordering of entity mappings within the mapping model.
Ultimately, I had to set options for -addPersistentStoreType:configuration:URL:options:error: to:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, nil];
thus omitting NSInferMappingModelAutomaticallyOption.