1.5 创建和修改自定义Managed Object(Core Data Programming Guide翻译)

这是苹果官方文档 Core Data Programming Guide 的渣翻译。

正如之前讨论的,managed object是累NSManagedObject的实例,或者NSManagedObject的子类的实例,它代表了一个Entity的实例。NSManagedObject是一个的、实现了所有managed object基本需求行为的类。你可以创建NSManagedObject的自定义子类,虽然这并不是常需要的。对于一个Entity,如果你不需要任何自定义的逻辑,你不必要为这个Entity创建一个自定义类。在某些时候,你可能需要实现一个自定义类,例如,为了提供一个自定义访问或者验证方法、使用不标准的字段、指定依赖的键、计算结果值、实现一些其他的自定义逻辑。

创建自定义Managed Object子类

在一个Objective-C managed object子类中,你可以在声明文件中声明模型化字段对应的属性,但是你不能声明实例变量。

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

注意那些属性被声明为nonatomic何strong。为了性能,Core Data不会复制对象值,甚至虽然这个值的类遵循了NSCopying protocal。

在Objective-C实现文件中,你需要修饰属性为dynamic:

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

在Swift中,你需要声明这些属性使用了@NSManaged关键字:

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

Core Data会根据这个managed object的Entity对应的数据模型,动态生成每个属性的高效的公共、私有的gett和setter访问方法、关系访问方法。

覆写方法指南

NSManagedObject它本身自定义了许多NSObject的特性,所以managed object可以很好地集成到Core Data的架构中。Core Data依赖于以下这些NSManagedObject实现的方法,你可能会进行覆写:

primitiveValueForKey:
setPrimitiveValue:forKey:
isEqual:
hash
superclass
class
self
zone
isProxy
isKindOfClass:
isMemberOfClass:
conformsToProtocol:
respondsToSelector:
managedObjectContext
entity
objectID
isInserted
isUpdated
isDeleted
isFault

不提倡覆写 initWithEntity:insertIntoManagedObjectContext:和description方法。如果在debug过程中description被置为fault,可能会导致不可预知的结果。你还应该不要覆写那些KVC方法,例如valueForKey: 和 setValue:forKeyPath:。

另外,在覆写awakeFromInsert、awakeFromFetch和验证方法例如validateForUpdate:之前,先调用父类的实现方法。对于覆写访问方法要十分小心,因为你可能会令性能受到不好的影响。

定义属性和数据存储

在某些方面,一个managed object操作起来就像一个字典——它是一个常见的容器对象,并且提供了NSEntityDescription对象绑定的属性的高效存储功能。NSManagedObject支持特定范围内的一些公共类型的字段值,包括string、date和number(更多细节请看NSAttributeDescription)。因此,你不需要在子类中定义实例变量。然而,如果你需要实现非标准的字段或者时区,你可能就需要。另外,如果你使用大型的二进制数据对象,可能会存在性能降低的问题——参考Binary Large Data Objects (BLOBs)。

使用非标准字段

在默认情况下,NSManagedObject在内部结构中把属性作为对象存储,通常来讲使用Core Data自己控制的存储方式比自定义实例变量要高效。

有些时候你可能需要使用一些不能直接支持的类型,例如color和C结构体。例如,在一个画图应用中你可能想要定义一个矩形Entity,这个Entity拥有一个color字段和一个bounds字段,这里color是NSColor的实例,bounds是NSRect的结构体。在这种情况下,你需要创建一个NSManagedObject的子类。

日期、时间和时区

NSManagedObject使用NSDate对象表示日期字段,内部使用基于格林尼治时间(GMT)的NSTimeInterval值存储时间值。不同时区的时间不是精确区分存储的——一般你应该使用一个基于GMT的Core Data日期来表示,这样在数据库内搜索才能统一标准化。如果你需要根据实际时区操作数据,你需要在数据模型中存储一个时区字段,这样你就需要创建一个NSManagedObject子类。

自定义初始化和释放

Core Data控制着managed object的生命周期。你不能用一个标准的Objective-C对象的理解,来假设一个managed object的生命周期——managed object会被框架在需要的时候实例化、销毁和唤醒。

当一个managed object被创建,会使用它所属的模型对象中的Entity的默认值来初始化。在许多情况下,在数据模型中配置的默认值是齐全的。然而,你可能希望做一些额外的初始化操作——可能使用dynamic值(例如现在的日期和时间)不能在模型中表现出来。

在一个标准的Objective-C类中,你通常覆写指定的初始化器(一般是init方法)。在一个NSManagedObject子类中,有3个不同的方法你可以用来自定义初始化——覆写initWithEntity:insertIntoManagedObjectContext:、awakeFromInsert 或 awakeFromFetch。不要覆写init。不提倡你覆写initWithEntity:insertIntoManagedObjectContext:,因为在这个方法中的状态改变可能不能很好地集成撤销和重做。另外两个方法, awakeFromInsert 和 awakeFromFetch,在以下两种不同的情况中可以区分开来:

  • awakeFromInsert在一个对象声明周期内只会被调用一次——第一次创建的时候。
    awakeFromInsert在你调用initWithEntity:insertIntoManagedObjectContext:或者insertNewObjectForEntityForName:inManagedObjectContext:之后会被立刻调用。你可以使用awakeFromInsert去初始化特殊的默认属性值,例如一个对象的创建日期,下面的例子会阐述:

OBJECTIVE-C

- (void)awakeFromInsert
{
    [super awakeFromInsert];
    [self setCreationDate:[NSDate date]];
}

SWIFT

override func awakeFromInsert() {
    super.awakeFromInsert()
    creationDate = NSDate()
}
  • awakeFromFetch在一个对象从持久化存储中重新初始化(在查询操作)的时候调用。
    例如,你可能覆写awakeFromFetch用以创建临时值和其他缓存。修改处理在此过程不可用,所以你可以很方便地使用公共的setter访问方法,而不用造成MOC中对象数据的混乱。然而,修改处理不可用表示,你不应该篡改关系,不然修改就不能正确地传递到目标对象上。为了替代覆写awakeFromFetch,你可以覆写awakeFromInsert或采用一些run loop相关的方法,例如performSelector:withObject:afterDelay:。

为了避免dealloc清除掉临时属性和其他变量,当一个对象转变为fault但是并未完全释放之前,覆写的didTurnIntoFault. didTurnIntoFault会被Core Data自动调用。你可以提前转变一个对象为fault,以降低内存消耗(详见Reducing Memory
Overhead),所以确保你在didTurnIntoFault中执行了清理操作是非常重要的。

你可能感兴趣的:(1.5 创建和修改自定义Managed Object(Core Data Programming Guide翻译))