The NSManagedObject is the object we work with the most in a Core Data application. Each instance of the NSManagedObject represents one entity in our Core Data repository. By combining Core Data with KVO (Key Value Observing) and KVC (Key Value Coding), this one object can dynamically represent any object that we need and that can be defined in our data model. KVO and KVC are discussed in great detail in Chapter 10, OS X: Bindings, KVC, and KVO. To learn more about them, I highly recommend you read Apple’s excellent documentation on the subjects.[2]
All of the properties and relationships defined in our data model are available and easy to access directly from the NSManagedObject. Without subclassing it, we can access values associated with an NSManagedObject in the following ways.
Attributes are the easiest to access. By utilizing KVC, we can get or set any attribute on the NSManagedObject directly. If you’ve reviewed our sample app in Chapter 1, Building a Foundation, you may have noticed that we didn’t write a Recipe class. At this point in our application, the NSManagedObject provides all of the required functionality. We could get the name as follows:
| NSManagedObject *recipe = ...; |
| NSString *name = [recipe valueForKey:@"name"]; |
Likewise, we can set the name in a similar fashion, as follows:
| NSManagedObject *recipe = ...; |
| [recipe setValue:@"New Name" forKey:@"name"]; |
When we want to subclass NSManagedObject, we can also define properties for the attributes (and relationships discussed in a moment) so that we can access them directly. In the header of the subclass, define the properties normally.
| @interface PPRRecipeMO : NSManagedObject |
| @property (strong) NSString *desc; |
| @property (strong) NSString *imagePath; |
| @property (strong) NSString *lastUsed; |
| @property (strong) NSString *name; |
| @property (strong) NSString *serves; |
| @property (strong) NSString *type; |
As you can see, we’re defining the property like normal, but there are no ivars (instance variables) associated with those properties. Since these properties are created dynamically at runtime, we don’t need to declare them in the header. However, we do need to flag them as dynamic so that the compiler won’t issue a warning. This is done in the implementation file.
| #import "PPRRecipeMO.h" |
| @implementation PPRRecipeMO |
| @dynamic desc; |
| @dynamic imagePath; |
| @dynamic lastUsed; |
| @dynamic name; |
| @dynamic serves; |
| @dynamic type; |
By declaring them as @dynamic, we’re telling the compiler to ignore any warnings associated with these properties because we promise to generate them at runtime. Naturally, if they turn up missing at runtime, our application is going to crash. However, when we’re working with NSManagedObject objects, the attributes will be looked up for us, and we don’t need to implement anything. By adding that code, we can access the attribute directly, as shown in the following example:
| PPRecipe *myRecipe = ...; |
| NSString *recipeName = [myRecipe name]; |
| //Do something with the name |
| [myRecipe setName:recipeName]; |
No warnings or errors will be generated with this code, and when our application runs we’ll be able to access the properties just like any “normal” subclass of NSObject.
It should be noted that accessing the attribute via KVC or properties will trigger KVO notifications that the attribute has changed. There are situations where we don’t want this to occur or where we prefer it to occur later. In those situations, we can access the attribute using the ‑primitiveValueForKey: and ‑setPrimitiveValue:forKey: methods. Both of these methods work the same as the ‑valueForKey: and ‑setValue:forKey methods that we’re used to working with, but they don’t cause KVO notifications to fire. This means the rest of our application will be unaware of any changes we make until and unless we notify it.
This is quite useful when you’re loading in data from an external source and the data is going to impact several attributes at once. Imagine we wrote a recipe importer that accepted a comma-separated value (CSV) file from another recipe application. In that situation, we might not want the UI or other parts of our application making decisions based on the data in the middle of the import. Therefore, we’d want to update the values without notifications, and once all of them were done, we’d let the notifications go out. The code to handle this situation would look like this:
| - (void)importData:(NSDictionary*)values //CSV translated into a dictionary |
| { |
| [self willChangeValueForKey:@"name"]; |
| [self willChangeValueForKey:@"desc"]; |
| [self willChangeValueForKey:@"serves"]; |
| [self willChangeValueForKey:@"type"]; |
| [self setPrimitiveValue:[values valueForKey:@"name"] forKey:@"name"]; |
| [self setPrimitiveValue:[values valueForKey:@"desc"] forKey:@"desc"]; |
| [self setPrimitiveValue:[values valueForKey:@"serves"] forKey:@"serves"]; |
| [self setPrimitiveValue:[values valueForKey:@"type"] forKey:@"type"]; |
| [self didChangeValueForKey:@"type"]; |
| [self didChangeValueForKey:@"serves"]; |
| [self didChangeValueForKey:@"desc"]; |
| [self didChangeValueForKey:@"name"]; |
| } |
In this example code, we’re handling all the change notifications ourselves and setting the values into our NSManagedObject directly using the ‑setPrimitiveValue:forKey: method. This will cause all the values to be updated prior to the notifications being fired.
Accessing relationships on an NSManagedObject is nearly as easy as accessing attributes. There’s a bit of a difference between working with a to-one relationship and a to-many relationship, though.
When we’re accessing a to-one relationship, we can treat it the same as we would an attribute. Since Core Data is first and foremost an object graph, a to-one relationship can be treated exactly like a property that contains any other object. For example, the relationship between RecipeIngredient and its Recipe is a to-one relationship from the RecipeIngredient side. Therefore, if we were accessing this relationship from that point of view, the code would be as follows:
| NSManagedObject *ingredient = ...; |
| NSManagedObject *recipe = [ingredient valueForKey:@"recipe"]; |
In this example, we’re using the ‑valueForKey: KVC method to access the relationship, and the NSManagedObject returns the object on the other side of the relationship, which is the entity. Likewise, to set the recipe for an ingredient, we’d use the following code:
| NSManagedObject *ingredient = ...; |
| NSManagedObject *recipe = ...; |
| [ingredient setValue:recipe forKey:@"recipe"]; |
This will set the object on the other side of the relationship.
The many side of a relationship is stored unordered. This means each time we fetch the objects on the many side of a relationship, the order isn’t guaranteed, and it’s probable that the order will change between fetches. However, we’re guaranteed that each object will be included only once. In other words, when we access a to-many relationship using KVC, we’ll get an NSSet back. For example, if we want to access the ingredients of a recipe, we’d use code similar to the following:
| NSManagedObject *recipe = ...; |
| NSSet *ingredients = [recipe valueForKey:@"ingredients"]; |
Likewise, setting the ingredients into a recipe is as follows:
| NSManagedObject *recipe = ...; |
| NSSet *someIngredients = ...; |
| [recipe setValue:someIngredients forKey:@"ingredients"]; |
This will set which ingredients are associated with the recipe.
You might notice that the NSSet we get back when accessing a to-many relationship is immutable. Adding an object to a to-many relationship with an immutable NSSet requires creating a mutable copy of the NSSet, adding the new object to the NSMutableSet, and setting the NSMutableSet back onto the parent object. It’s a painful process and, fortunately, unnecessary. When we want to add an object to a to-many relationship, we can use ‑mutableSetValueForKey: in the place of ‑valueForKey:. This returns an NSMutableSet for us that’s already associated with the parent object and reduces our code to the following:
| NSManagedObject *newIngredient = ...; |
| NSManagedObject *recipe = ...; |
| NSMutableSet *ingredients = [recipe mutableSetValueForKey:@"ingredients"]; |
| [ingredients addObject:newIngredient]; |
Note that we didn’t need to set the NSMutableSet back into the entity, and therefore the code to add an object to a to-many relationship is quite short.
One important thing to notice in these relationship examples is that when we update the relationship, we’re updating only one side of it. Because we defined these relationships as double-sided (that is, they include an inverse relationship that we defined in Introducing the NSManagedObjectModel), Core Data handles keeping the integrity of the relationships intact. When we update one side of a relationship, Core Data automatically goes in and sets the other side for us.
Similar to the process of accessing attributes discussed earlier, changes to a relationship will fire KVO notifications. Since there are situations in which we don’t want this to occur or in which we want a finer-grained control over the notifications, there are primitive accessors for relationships as well. However, there is no primitive method for retrieving an NSMutableSet for a to-many relationship. Therefore, if the code requires changes to a relationship with either delayed or no observations being fired, we must use ‑primitiveValueForKey: to get back an NSSet, call ‑mutableCopy on the NSSet, add our new object to the NSMutableSet, and finally use ‑setPrimitiveValue:forKey: to apply the changes.
Relationships can use properties, just like the attributes discussed earlier. In the code in Mutable Access of To-Many Relationships, if we want to add a property to retrieve the relationship, we declare the following property:
| @property (strong) NSSet *recipeIngredients; |
And then we flag it as dynamic in the implementation file.
Although NSManagedObject provides a tremendous amount of flexibility and handles the majority of the work a data object normally does, it doesn’t cover every possibility, and there are occasions where we might want to subclass it. Subclassing to gain @property access to attributes and relationships is one common situation, but we may also want to add other convenience methods or additional functionality to the object. When such a situation arises, you must keep in mind a few general rules.
In Apple’s documentation, the methods in the table that follows should never be overridden.
‑primitiveValueForKey: | ‑setPrimitiveValue:forKey: | ‑isEqual: |
‑hash | ‑superclass | ‑class |
‑self | ‑zone | ‑isProxy: |
‑isKindOfClass: | ‑isMemberOfClass: | ‑conformsToProtocol: |
‑respondsToSelector: | ‑retain | ‑release |
‑autorelease | ‑retainCount | ‑managedObjectContext |
‑entity | ‑objectID | ‑isInserted |
‑isUpdated | ‑isDeleted | ‑isFault |
‑alloc | ‑allocWithZone: | +new |
+instancesRespondToSelector: | +instanceMethodForSelector: | ‑methodForSelector: |
‑methodSignatureForSelector: | ‑isSubclassOfClass: |
It’s quite a list. Most, if not all, of these methods are common sense, and experience with Objective-C explains why they shouldn’t be overridden. Even though this is a fairly long list, I’m going to add a few more.
-initXXX
The first is ‑initXXX. There’s really no reason or benefit to overriding the ‑init methods of an NSManagedObject, and there are situations in which doing so has unpredictable results. Although it’s not specifically against the documentation to override the ‑init methods, I recommend strongly against it. The ‑awakeFromInsert and ‑awakeFromFetch methods provide sufficient access that overriding ‑init is unnecessary. (Both ‑awakeFromInsert and ‑awakeFromFetch are discussed in more depth later in this chapter.)
KVO Methods
I’d also add all of the KVO methods. The documentation flags these methods as “discouraged,” but I’d put them right in the “do not subclass” list. There’s no reason to override these methods, and any logic that you’d want to put into them can probably be put somewhere else with fewer issues.
-description
In addition, there’s the ‑description method, used fairly often in logging. It’s a great way to dump the contents of an object out to the logs during debugging. However, when we’re dealing with faults (discussed in Chapter 5, Performance Tuning), we don’t want to fire a fault in the ‑description method. Since the default implementation of ‑description does the right thing with regard to faults, it’s best that we avoid overriding its behavior.
Core Data faults are similar to virtual memory page faults. Faulted objects are scoped objects that may or may not actually be in memory, or “realized,” until you actually use them. Although there’s no guarantee for when a faulted NSManagedObject will be loaded into memory, it’s guaranteed to be loaded when accessed. However, the object will be an instance of the appropriate class (either an NSManagedObject or the designated subclass), but its attributes aren’t initialized.
With the long list of methods that we shouldn’t override, what methods should we consider overriding? There are a few methods we’ll commonly override.
-didTurnIntoFault
This method is called after the NSManagedObject has been turned into a fault. It’s a good place to release transient resources. One important thing to note is that when this method is called, all the stored values/relationships in the NSManagedObject are already out of memory. If you access any of them, it will fire the fault and pull them all back into memory again.
-willTurnIntoFault
Similar to ‑didTurnIntoFault, this method is called just before the NSManagedObject is turned into a fault. If your code needs to access attributes or relationships on the NSManagedObject before it’s turned into a fault, then this is the entry point to use. Transient resources that impact attributes and relationships should be released here.
-awakeFromInsert
As mentioned, overriding any of the ‑init methods is risky and unnecessary. However, it’s very useful to be able to prepare an NSManagedObject before it starts accepting data. Perhaps we want to set up some logical defaults or assign some relationships before handing the object to the user. In these situations, we use ‑awakeFromInsert. As the name implies, this method is called right after the NSManagedObject is created from an insert call. This method is called before any values are set and is a perfect opportunity to set default values, initialize transient properties, and perform other tasks that we would normally handle in the ‑init method. This method is called exactly once in the entire lifetime of an object. It won’t be called on the next execution of the application, and it won’t be called when an object is read in from the persistent store. Therefore, we don’t need to worry about overriding values that have been set previously. When we override this method, we should be sure to call [super awakeFromInsert] at the very beginning of our implementation to allow the NSManagedObject to finish anything it needs to before we begin our code.
-awakeFromFetch
‑awakeFromFetch is the counterpart to ‑awakeFromInsert. The ‑awakeFromFetch method will be called every time the object is retrieved from the persistent store (that is, loaded from disk). This method is highly useful for setting up transient objects or connections that the NSManagedObject will use during its life cycle. At this point in the creation of the NSManagedObject, the observation of changes to the object (or other objects) is turned off, and Core Data won’t be aware of any changes made. Ideally, we should avoid making any changes to relationships during this method because the inverse will not be set. However, if we explicitly set both sides of the relationship, it’s possible to make changes here. Like the ‑awakeFromInsert method, when we override this method, we should call [super awakeFromFetch]; before any of our own code is called.
Now that we’ve explored creating and accessing instances of NSManagedObject, we’ll look at how to retrieve them.