美文网首页
Core Data编程指南 1-6: 创建和修改数据对象

Core Data编程指南 1-6: 创建和修改数据对象

作者: TalktoEason | 来源:发表于2017-07-19 15:23 被阅读0次

    Core Data中,我们将NSManagedObject或者其子类的对象称为数据对象,代表着数据表中的数据记录。NSManagedObject是数据对象的基类,提供了一系列基本操作。通常情况下,我们可以继承这个类来添加自定义的数据校验方法、非标准类型的数据字段、特殊的依赖关系或者各种中间值等。如果不需要自定义这些操作,一般不需要继承NSManagedObject类。

    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

    在Objective-C中,我们可以在子类的头文件中声明自定义属性,但不需要声明对应的实体变量。

    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:

    @interface MyManagedObject : NSManagedObject
    
    @property (nonatomic, strong) NSString *title;
    @property (nonatomic, strong) NSDate *date;
    
    @end
    

    这些属性被声明成nonatomicstrong类型。为了保证运行效率,即使子类实现了NSCopying protocol, Core Data在任何时候都不会直接拷贝对象值。

    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.

    在Objective-C的实现文件中,这些属性被标志为dynamic

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

    @implementation MyManagedObject
    @dynamic title;
    @dynamic date;
    @end
    

    在 Swift中,我们可以使用@NSManaged标签来声明这些属性。

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

    class MyManagedObject: NSManagedObject {
        @NSManaged var title: String?
        @NSManaged var date: NSDate?
    }
    

    Core Data会根据数据层模型定义的数据结构,为这些属性生成public的get、set方法。一般来说,你并不需要自定义这些访问方法。

    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

    为了适配Core Data的基础架构,NSManagedObject重载了很多NSObject的方法。下面这些NSManagedObject方法是和Core Data架构紧密相关的,因此尽量不要重载它们。

    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

    同样也不推荐重载 initWithEntity:insertIntoManagedObjectContext:description方法。如果在调试的时候description方法输出错误信息,可能会导致一些不可预测的结果。任何和键值对相关的方法一般也不允许重载,例如valueForKey: and setValue:forKeyPath:

    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:.

    此外,重载awakeFromInsert, awakeFromFetch方法,或者数据校验方法 validateForUpdate:的时候,切记要调用父类的原始方法。重载任何方法都需要特别注意运行时的效率问题。

    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

    从某种角度上看,NSManagedObject类似一个dictionary结构,作为一个基本容器,它为NSEntityDescription对象定义的各种不同类型的属性提供了存储空间。NSManagedObject 支持一些基本的字段类型,包括字符串、日期,时区和数字等(参见 NSAttributeDescription)。因此除非是一些非标准的数据字段,我们通常不需要在子类中自定义特殊的变量。一个比较特殊的例子是大块的二进制数据,有些时候我们需要在子类中处理一些和运行效率相关的功能,参见 Binary Large Data Objects (BLOBs)

    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

    NSManagedObject将属性值以对象的形式存储在内部。一般来说,Core Data在使用内部的存储结构时,比自定义的外部变量更有效率。

    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.

    有一些时候,我们需要处理一些非标准的数据结构,比如颜色值或者C语音的结构类型。例如在一个图形应用程序中,一个矩形对象会有颜色和形状两个属性,分别是NSColorNSRect这两种非标准类型。这时,我们需要扩展NSManagedObject

    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支持标准的日期(NSDate)和时间段( NSTimeInterval )类型。日期属性存储到数据库中时会被变换成GMT标准时间,方便进行统一的查询、对比操作。但其中的时区信息会丢失。如果需要保存时区信息,我们需要定制化自己的NSManagedObject子类。

    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会管理数据对象的整个生命周期。和Objective-C对象不同,Core Data的数据对象除了初始化和销毁过程外,还可以通过faulting和undo操作进行回溯。

    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.

    在传统的Objective-C类中,我们通过重载构造函数(通常是init方法)来定制化初始化过程,但在NSManagedObject的子类中,需要用到其他三种不同的方法 -- initWithEntity:insertIntoManagedObjectContext:, awakeFromInsert, 或者awakeFromFetch。而init方法尽量不要去重载的。也不推荐重载initWithEntity:insertIntoManagedObjectContext:方法,这个函数中的一些状态迁移可能会影响undo和redo功能。另两个函数awakeFromInsert, 或者awakeFromFetch则适用在不同场合:

    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:

    OBJECTIVE-C
    -----------
    
    - (void)awakeFromInsert
    {
        [super awakeFromInsert];
        [self setCreationDate:[NSDate date]];
    }
    
    SWIFT
    -----
    
    override func awakeFromInsert() {
        super.awakeFromInsert()
        creationDate = NSDate()
    }
    
    • 对象从外部存储器中装载到内存时(比如查询操作)会调用awakeFromFetch方法。

      你可以重载awakeFromFetch去初始化一些中间值,或者其他缓存。在awakeFromFetch中,一些跟修改属性值相关的衍生操作是被关闭的,你可以调用对象的set方法去设置属性值,但这些改动不会被校验,也不回被传播到其他对象中去。因此不应该在awakeFromFetch中修改任何关系,否则会引起相关数据不一致。awakeFromFetch的重载效果也可以通过重载awakeFromInsert,并实现一些和运行线程相关的方法,例如performSelector:withObject:afterDelay:来实现。

    • 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 awakeFromInsertor employ any of the run loop-related methods such as performSelector:withObject:afterDelay:.

    当我们需要在对象销毁的时候清除一些中间变量的时候,需要重载didTurnIntoFault方法,而不是 dealloc 方法。Core Data在一个数据对象进入fault状态等待被销毁的时候会自动调用didTurnIntoFault方法。我们也可以将一个数据对象主动设置成fault状态去减少当前的内存开销(参见 Reducing Memory Overhead)。因此在didTurnIntoFault方法中进行正确的清理操作是非常重要的。

    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来自动生成数据子类
    Xcode Generated Subclasses

    Xcode 8, iOS 10和macOS 10.12之后的版本,在编辑数据层模型的过程中就可以自动生成NSManagedObject的子类或者extensions/categories。

    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. 确认工具的版本大于Xcode 8.0
      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.

    3. 选择是否要生成子类,或是Category/Extension。
      Set the code generator to either None, Class Definition, or Category/Extension.

    image: ../Art/CodeGen_2x.pngimage: ../Art/CodeGen_2x.png

    当配置完成后,Xcode会自动生成我们需要的子类或者categories/extensions。

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

    备注 NOTE

    自动生成的代码并不会包含在工程中,而是作为编译流程的临时文件。我们可以在编译目录中找到这些文件。这些文件会被反复生成,手动修改这些文件不会起到任何作用。

    The generated source code is not included in your project and is intended to be a part of the build process. You will not see the files in your project’s source list but the files can be reviewed in the build directory. These files can be regenerated often so there is no value in editing them manually.

    我们可以在单独的代码文件中通过category(在Objective-C中)或者extension (在Swift中),向生成的子类添加各种辅助功能和事务逻辑。

    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.

    相关文章

      网友评论

          本文标题:Core Data编程指南 1-6: 创建和修改数据对象

          本文链接:https://www.haomeiwen.com/subject/nrokkxtx.html