CoreData 中的继承与表结构设计的权衡
在设计 Core Data 模型时,很自然地会想到使用继承来抽取共享的属性,以减少重复并保持代码的整洁。例如,我们可能会想为每个模型添加 createdate
和 updatedate
字段以跟踪它们的创建和更新时间。下面我们将探讨在 Core Data 中如何实现继承,并指出使用继承可能导致的一个主要问题。
实现继承
创建基类实体: 在你的
xcdatamodeld
文件中创建一个名为BaseEntity
的新实体,并在其中定义createdate
和updatedate
两个属性,将它们的类型设置为Date
.设置其他实体的父类: 选择你想要继承
createdate
和updatedate
属性的每个实体,并在“父类”设置中选择BaseEntity
.生成
NSManagedObject
子类: 为你的实体生成NSManagedObject
子类。现在BaseEntity
以及从它继承的所有其他实体都应包括createdate
和updatedate
属性。覆盖
awakeFromInsert
和willSave
方法 (可选): 在BaseEntity
中覆盖awakeFromInsert
和willSave
方法以确保createdate
和updatedate
在适当的时候被设置。
import Foundation
import CoreData
@objc(BaseEntity)
public class BaseEntity: NSManagedObject {
@NSManaged public var createdate: Date?
@NSManaged public var updatedate: Date?
public override func awakeFromInsert() {
super.awakeFromInsert()
self.createdate = Date()
}
// 不能在这里更新 updatedate
//public override func willSave() {
// super.willSave()
// self.updatedate = Date()
//}
public override func didChangeValue(forKey key: String) {
super.didChangeValue(forKey: key)
if key != #keyPath(createdate) {
createdate = Date()
}
}
}
继承带来的问题
虽然继承在许多情况下都是有用的,但在 Core Data 中使用它可能会导致一个不太理想的结果:所有继承自同一个父类的实体都会被存储在同一个表中,这种行为被称为“单表继承”(Single Table Inheritance)。虽然这种机制有助于保持数据模型的简单和高效,但可能不适用于所有情况,特别是当我们想要为每个模型创建一个单独的表时。
替代方案
为了避免“单表继承”而又能够实现通用字段的需求,我们可以考虑以下两种替代方案:
- 代码重用:
创建一个协议或扩展来重用
createdate
和updatedate
的逻辑,而不是通过继承来实现。例如,我们可以创建一个TimeStamped
协议,并为NSManagedObject
添加一个扩展来实现这个协议。
protocol TimeStamped {
var createdate: Date? { get set }
var updatedate: Date? { get set }
}
extension NSManagedObject: TimeStamped {
@NSManaged public var createdate: Date?
@NSManaged public var updatedate: Date?
func updateTimestamps() {
if self.isInserted {
self.createdate = Date()
}
if self.isUpdated {
self.updatedate = Date()
}
}
}
// 在保存操作之前调用 updateTimestamps
viewContext.observe(\.hasChanges) { context, change in
if context.hasChanges {
for object in context.insertedObjects.union(context.updatedObjects) {
(object as? TimeStamped)?.updateTimestamps()
}
}
}
- 手动添加属性:
对于每个实体,手动添加
createdate
和updatedate
属性。虽然这可能会增加一些重复的工作,但它可以确保每个实体都有自己的表。
通过深入了解 Core Data 中继承的工作机制以及可能出现的问题,我们可以更加明智地决定如何设计我们的数据模型,以满足应用的需求同时保持代码的整洁和可维护性。