Documentation Archive

Developer

Core Data Programming Guide

On This Page

Creating and Modifying Custom Managed Objects

As discussed previously, managed objects are instances of the NSManagedObject class, or of a subclass of NSManagedObject, that represent instances of an entity. NSManagedObject is a generic class that implements all the basic behavior required of a managed object. You can create custom subclasses of NSManagedObject, although this is often not required. If you do not need any custom logic for a given entity, you do not need to create a custom class for that entity. You implement a custom class to, for example, provide custom accessor or validation methods, use nonstandard attributes, specify dependent keys, calculate derived values, and implement any other custom logic.

Creating Custom Managed Object Subclasses

In an Objective-C managed object subclass, you can declare the properties for modeled attributes in the interface file, but you don’t declare instance variables:

  1. @interface MyManagedObject : NSManagedObject
  2. @property (nonatomic, strong) NSString *title;
  3. @property (nonatomic, strong) NSDate *date;
  4. @end

Notice that the properties are declared as nonatomic and strong. For performance reasons, Core Data typically does not copy object values, even if the value class adopts the NSCopying protocol.

In the Objective-C implementation file, you specify the properties as dynamic:

  1. @implementation MyManagedObject
  2. @dynamic title;
  3. @dynamic date;
  4. @end

In Swift, you declare the properties using the @NSManaged keyword:

  1. class MyManagedObject: NSManagedObject {
  2. @NSManaged var title: String?
  3. @NSManaged var date: NSDate?
  4. }

Core Data dynamically generates efficient public and primitive get and set attribute accessor methods and relationship accessor methods for properties that are defined in the entity of a managed object’s corresponding managed object model. Therefore, you typically don’t need to write custom accessor methods for modeled properties.

Guidelines for Overriding Methods

NSManagedObject itself customizes many features of NSObject so that managed objects can be properly integrated into the Core Data infrastructure. Core Data relies on NSManagedObject’s implementation of the following methods, which you should therefore not override:

  • primitiveValueForKey:

  • setPrimitiveValue:forKey:

  • isEqual:

  • hash

  • superclass

  • class

  • self

  • zone

  • isProxy

  • isKindOfClass:

  • isMemberOfClass:

  • conformsToProtocol:

  • respondsToSelector:

  • managedObjectContext

  • entity

  • objectID

  • isInserted

  • isUpdated

  • isDeleted

  • isFault

You are discouraged from overriding initWithEntity:insertIntoManagedObjectContext: and description. If description fires a fault during a debugging operation, the results may be unpredictable. You should typically not override the key-value coding methods such as valueForKey: and setValue:forKeyPath:.

In addition, before overriding awakeFromInsert, awakeFromFetch, and validation methods such as validateForUpdate:, invoke their superclass implementation. Be careful when overriding accessor methods because you could negatively impact performance.

Defining Properties and Data Storage

In some respects, a managed object acts like a dictionary—it is a generic container object that efficiently provides storage for the properties defined by its associated NSEntityDescription object. NSManagedObject supports a range of common types for attribute values, including string, date, and number (see NSAttributeDescription for full details). Therefore, you typically do not need to define instance variables in subclasses. However, if you need to implement nonstandard attributes or preserve time zones, you may need to do so. In addition, there are some performance considerations that can be mitigated in a subclass if you use large binary data objects—see Binary Large Data Objects (BLOBs).

Using Nonstandard Attributes

By default, NSManagedObject stores its properties as objects in an internal structure, and in general Core Data is more efficient working with storage under its own control than with using custom instance variables.

Sometimes you need to use types that are not supported directly, such as colors and C structures. For example, in a graphics application you might want to define a Rectangle entity that has attributes color and bounds, which are instances of NSColor and NSRect structures respectively. This situation requires you to create a subclass of NSManagedObject.

Dates, Times, and Preserving Time Zones

NSManagedObject represents date attributes with NSDate objects, and stores times internally as an NSTimeInterval value that is based on GMT. Time zones are not explicitly stored—always represent a Core Data date attribute in GMT, so that searches are normalized in the database. If you need to preserve the time zone information, store a time zone attribute in your model, which may require you to create a subclass of NSManagedObject.

Customizing Initialization and Deallocation

Core Data controls the life cycle of managed objects. With faulting and undo, you cannot make the same assumptions about the life cycle of a managed object that you do with a standard Objective-C object—managed objects can be instantiated, destroyed, and resurrected by the framework as it requires.

When a managed object is created, it is initialized with the default values given for its entity in the managed object model. In many cases the default values set in the model are sufficient. Sometimes, however, you may wish to perform additional initialization—perhaps using dynamic values (such as the current date and time) that cannot be represented in the model.

In a typical Objective-C class, you usually override the designated initializer (often the init method). In a subclass of NSManagedObject, there are three different ways you can customize initialization—by overriding initWithEntity:insertIntoManagedObjectContext:, awakeFromInsert, or awakeFromFetch. Do not override init. It is also recommended that you do not override initWithEntity:insertIntoManagedObjectContext:, as state changes made in this method may not be properly integrated with undo and redo. The two other methods, awakeFromInsert and awakeFromFetch, allow you to differentiate between two different situations:

  • awakeFromInsert is invoked only once in the lifetime of an object—when it is first created.

    awakeFromInsert is invoked immediately after you invoke initWithEntity:insertIntoManagedObjectContext: or insertNewObjectForEntityForName:inManagedObjectContext:. You can use awakeFromInsert to initialize special default property values, such as the creation date of an object, as illustrated in the following example.

    Objective-C

    1. - (void)awakeFromInsert
    2. {
    3. [super awakeFromInsert];
    4. [self setCreationDate:[NSDate date]];
    5. }

    Swift

    1. override func awakeFromInsert() {
    2. super.awakeFromInsert()
    3. creationDate = NSDate()
    4. }
  • awakeFromFetch is invoked when an object is reinitialized from a persistent store (during a fetch).

    You can override awakeFromFetch to, for example, establish transient values and other caches. Change processing is explicitly disabled in awakeFromFetch so that you can conveniently use public set accessor methods without dirtying the object or its context. This disabling of change processing does mean, however, that you should not manipulate relationships because changes will not be properly propagated to the destination object or objects. Instead of overriding awakeFromFetch, you can override awakeFromInsert or employ any of the run loop-related methods such as performSelector:withObject:afterDelay:.

Avoid overriding dealloc to clear transient properties and other variables. Instead, override didTurnIntoFault. didTurnIntoFault is invoked automatically by Core Data when an object is turned into a fault and immediately prior to actual deallocation. You might turn a managed object into a fault specifically to reduce memory overhead (see Reducing Memory Overhead), so it is important to ensure that you properly perform cleanup operations in didTurnIntoFault.

Xcode Generated Subclasses

Starting with Xcode 8, iOS 10, and macOS 10.12, Xcode can automatically generate NSManagedObject subclasses or extensions/categories from the Core Data Model.

To enable this feature in an existing project, first ensure that the data model is configured correctly:

  1. Select the Core Data Model file, and open the File inspector.

  2. Confirm that the Tools Version is set to Xcode 8.0 or later.

  3. Confirm that the Code Generation is set to the language you are currently using.

After the data model is configured, you can then configure each entity:

  1. Select the entity you want to configure.

  2. Open the Data Model inspector.

    image: ../Art/CodeGen_2x.png
  3. Set the code generator to either None, Class Definition, or Category/Extension.

After the data model is configured, Xcode regenerates the subclasses or categories/extensions whenever the related entity has changed in the data model.

If you wish to add additional convenience methods or business logic to your NSManagedObject subclasses, you can create a category (in Objective-C) or an extension (in Swift) and place the additional logic there.