核心数据中具有自定义类型的所有属性都必须是一种关系吗?

时间:2018-12-31 17:20:31

标签: json swift core-data codable nsvaluetransformer

这是来自以下How to map nested complex JSON objects and save them to core data?中的评论的后续问题。

想象一下我已经为我的应用准备了这段代码。

class Passenger{
    var name: String
    var number: String
    var image : UIImage
    // init method
}
class Trip {
    let tripNumber : Int
    let passenger : Passenger

    init(tripNumber: Int, passenger: Passenger) {
        self.tripNumber = tripNumber
        self.passenger = passenger
    }
}

现在,我决定为我的应用添加持久性。我有一张行程表。我想显示出行旅客,但不需要表格直接查询旅客。这只是旅行的自定义对象/属性。每次访问旅客时,都是通过Trips。

因此,有一种方法可以创建一个名为'TripEntity'的NSManagedObject新子类并存储我的乘客-无需1.为'Passenger'创建另一个NSManagedObject子类2.创建一个与Passenger和Trip之间成反比关系的关系?简而言之,我只是希望它成为一个属性。从概念上来说,这也只是一个属性。这不是真正的关系...

还是在使用Core-data之后,每个自定义类型都需要显式地成为NSManagedObject的子类?否则,它将不会持续存在。我猜这也是对象 graph 的意思。您的图形需要完整。图外的任何内容都将被忽略...

我之所以这样问,是因为我实际上要存储的JSON对象是巨大的,并且我正在尝试减少需要完成的工作。

2 个答案:

答案 0 :(得分:3)

您可以将乘客添加到一个Trip实体中,但是由于属性类型受到限制,您必须使用transformable类型,这对于归档和取消归档对象可能非常昂贵。

如果源数据是JSON,最有效的方法是为PassengerTrip创建Core Data实体并添加逆关系。然后,使所有NSManagedObject类都采用Codable,并向每个类添加init(from decoderencode(to encoder:方法。

例如,假设Trip类与Passenger有很多关系,

@NSManaged public var tripNumber: Int32
@NSManaged public var passengers: Set<Passenger>

并且在Passenger类中有一个属性trip

@NSManaged public var trip: Trip?

这是Decodable中必需的Trip初始化程序。解码器可以解码数组和集合。

private enum CodingKeys: String, CodingKey { case tripNumber, passengers }

public required convenience init(from decoder: Decoder) throws {
    guard let context = decoder.userInfo[.context] as? NSManagedObjectContext else { fatalError("Context Error") }
    let entity = NSEntityDescription.entity(forEntityName: "Trip", in: context)!
    self.init(entity: entity, insertInto: context)
    let values = try decoder.container(keyedBy: CodingKeys.self)
    tripNumber = try values.decode(Int32.self, forKey: .tripNumber)
    passengers = try values.decode(Set<Passenger>.self, forKey: .passengers)
    passengers.forEach{ $0.trip = self }
}

通过forEach行设置逆关系。
您必须添加扩展名JSONDecoder才能在NSManagedObjectContext对象中传递当前的userInfo

相应的编码器非常简单-

public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(tripNumber, forKey: .tripNumber)
    try container.encode(passengers, forKey: .passengers)
}

NSManagedObject类采用Codable非常适合从JSON数据预填充数据库或进行JSON备份。

答案 1 :(得分:1)

任何自定义属性都必须可以表示为“核心数据”属性类型之一。其中包括显而易见的东西,例如字符串和数值。它还包含“二进制”,即可以与NSData(在Swift中为Data)之间进行转换的任何东西。

根据您的描述,最好的方法可能是

  1. 为您的NSCoding课程采用Passenger
  2. 使passenger属性成为Core Data“可转换”类型。 (顺便说一句,这应该是一排乘客吗?您有一个相关的乘客,但是您的问题描述的是好像有一个以上的乘客)。
  3. 执行#2之后,将乘客属性的“自定义类”字段设置为您的类的名称-Passenger

如果您做这两件事,Core Data将自动调用NSCoding方法,以在您的Passenger类和可以保存在Core Data中的二进制Blob之间进行转换。