1. Core Data Classes

    The very basic functionality of Core Data is to store objects. Each object should be defined presenting the entities to be persisted, and the relationships between them.

    Core Data provide a set of classes to deal with this whole “model definition” issue.

    Model

    * `NSManagedObjectModel`: A model is where we describe a collection of entities. Is like an *schema* 
    * `NSEntityDescription`: Each of the entities described in the model. The entity description contains the name, and the managed object class name.  
    * `NSPropertyDescription`: This is an abstract class. `NSAttributeDescription`, `NSRelationshipDescription` and `NSFetchedPropertyDescription` are the concrete subclasses used to describe the composition of each entity. 
    

    For instance, in the almost canonical library example, the model might be composed for a set of entities, one for Books, one for Authors, one for Lenders, etc. The lender entity should have at least the lender’s name, and perhaps some address, these two are NSAttributeDescription. The book entity should contain a name NSAttributeDescription, an author NSRelationshipDescription, and a checkout property to know when the has been checkout. The author entity should be composed by the author’s name, and the books NSRelationshipDescription. And perhaps the lender should be completed with a NSFetchedPropertyDescription of books available to lend.

    Attributes are scalar, and strong typed. The attributes types are defined in NSAttributeDescriptions.h

    Name Name in Xcode Editor Cocoa Instantiation Description
    NSInteger16AttributeType Integer 16 NSNumber 16-bit integer
    NSInteger32AttributeType Integer 32 NSNumber 32-bit integer
    NSInteger64AttributeType Integer 64 NSNumber 64-bit integer
    NSDecimalAttributeType Decimal NSDecimalNumber base-10 object wrapper
    NSDoubleAttributeType Double NSNumber double object wrapper
    NSFloatAttributeType Float NSNumber float object wrapper
    NSStringAttributeType String NSString string of characters
    NSBooleanAttributeType Boolean BOOL YES / NO
    NSDateAttributeType Date NSDate Date object
    NSBinaryDataAttributeType Binary NSData Raw Data
    NSTransformableAttributeType Transformable Non Standard Type Non Standard Type

    If your attribute is of NSTransformableAttributeType, the attributeValueClassName must be set or attribute value class must implement NSCopying.

    Knowing how models are represented as objects is important especially if you want to create custom data stores, or even generate models dynamically at runtime.

    Store

    The NSPersistentStoreCoordinator will create and bind the, ehem, persistent store, and then we can create the NSManagedObjectContext. The persistent store coordinator acts as a mediator between the managed object and the actual persistent stores. This context is our way to interact with the graph, we are going to query the store, and add new items to it, through the NSManagedObjectContext.

    The NSPersistenStoreCoordinator is initialized using the NSManagedObjectModel. Once the coordinator is loaded, it can register all the persistent stores.

    Cocoa offers four types of persistent stores by default.

    Name Description Available in iOS
    NSSQLiteStoreType SQLite backed database Yes
    NSBinaryStoreType Binary file Yes
    NSInMemoryStoreType Stored only in memory, not in the file system Yes
    NSXMLStoreType XML formatted file No

    Most of the time, if not always, a typical user will choose NSSQLiteStoreType, thou NSInMemoryStoreType might be excellent for a memory cache as well.

    Context

    Once the persistent store coordinator is created, we can initialize an instance of NSManagedObjectContext to coordinate the creation, edition or deletion of managed objects. Many ask why we need a managed context, why not save everything to the store. The reason is simple, performance. It makes no sense to hit twice the store to retrieve the same information, or to bloat the memory with data that we don’t need. The context coordinate the many queries, and will try to balance the load. The context will not only controls the access to the store, but will provide a undo / redo system, very similar to the one used in the UI.

    Managed objects supports key-value coding. Perhaps the simplest way to access using valueForKey: or setValue:ForKey:. We can say that the subclasses of NSManagedObject are the ones are the data that we describe using the NSEntityDescription.

    Managed objects also supports key-value observing. This is not a pattern exclusive to Core Data, but is extremely useful. Instead f querying an object to know if the value of some property has been changed, we register as and observer, and hear either willChangeValueForKey: or didChangeValueForKey:.

    [someManagedObjectInstance addObserver:observerObject 
                                forKeyPath:@"propertyName" 
                                   options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) 
                                   context:nil]
    

    The options parameter specifies the information that is provided to the observer when a change notification is sent. Using the option NSKeyValueObservingOptionOld specifies that the original object value is provided to the observer as an entry in the change dictionary. Specifying the NSKeyValueObservingOptionNew option provides the new value as an entry in the change dictionary. To receive both values, you would bitwise OR the option constants.

    In this case, context is not an instance of NSManagedObjectContext, but a pointer is a C pointer or an object reference. The context pointer can be used as a unique identifier to determine the change that is being observed, or to provide some other data to the observer. The context pointer is not retained, and it is the responsibility of the application to ensure that it is not released before the observing object is removed as an observer. The observing object is not retained either.

    To receive notifications, the observerObject has to implement:

    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary *)change
                           context:(void *)context; 
    

    Requests

    To retrieve information from the context, we use NSFetchRequest instances. Fetch requests are composed by to elements, an NSPredicate and an NSSortDescriptor. These two classes (that belongs to foundation, and are used, for instance, to filter arrays), specify the constrains we want to impose in the query. It is not mandatory to use either of them. The query is not filtered if you avoid the NSPredicate and they are not sorted if you leave out the NSSortDescriptor.

    NSMutableArray *predicates = [NSMutableArray array];
    [predicates addObject:[NSPredicate predicateWithFormat:@"someProperty >= %@", lBoundary]];
    [predicates addObject:[NSPredicate predicateWithFormat:@"owner <= %@", rBoundary]];
    NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
    
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:@"entityName" inManagedObjectContext:context]];
    [request setPredicate:predicate];
    NSArray *results = [context executeFetchRequest:request error:nil];
    

Notes

  1. metalkin reblogged this from volonbolon
  2. rexlog reblogged this from volonbolon
  3. cocoaheads reblogged this from volonbolon
  4. do-nothing reblogged this from volonbolon
  5. volonbolon posted this