这一节,我们来了解如何表达 ENTITY 之间的关系。为了演示,我们创建下面这样的关系:

其中:

  • Series 表示视频所在的系列,一个系列可以包含多个视频,这是一个一对多的关系。这种关系,Core Data 用一个双箭头指向关系中,多的一方;
  • Episode 表示视频,一个视频只能属于一个系列,这是一个一对一的关系;
  • History 表示某个账号的视频观看记录,一个记录只能对应一个视频,这也是一个一对一的关系;

Series 和 Episode

首先,选中 CoreData.xcdatamodeld,在其中创建一个表示视频系列的 ENTITY:Series:

它有三个属性:

  • id:表示数据库中视频系列记录的 ID;
  • title:表示视频系列的标题;
  • summary:表示视频系列的简介;

其次,把之前创建的 Episode ENTITY 改成下面这样:

其中:

  • id:表示数据库中该视频的 ID;
  • seriesId:表示该视频所属的视频系列;
  • title 和 summary 则表示视频的标题和简介;

创建好了这两个 ENTITY 之后,就可以创建它们之间的关系了。先选中 Series,点击 Relationships 下面的 +,创建一个叫做 episodes 的关系,它的 Destination 设置成 Episode:

然后,再选中 Episode,给它创建一个 Destination 为 Series 的关系,并把它的 inverse 设置成 episodes:

这时,再回到 Series,就会发现之前创建的 episodes 也有了对应的 inverse:

接下来,为了表达 Series 到 Episode 一对多的关系,我们选中 Series 中的 episodes 关系,在 Show the Data Model inspector 中,把 Type 设置成 To Many:

另外,在上图中还可以看到,我们把 Delete Rule 设置成了 Cascade。这也是在创建关系时,一定要认真考虑的设置。它表示从 Core Data 中删除一个关系的 Source 时,对 Destination 采取的动作。Xcode 提供了几种不同的选项:

  • Deny:表示只要关系的目标还存在,就禁止删除 Source;
  • Nullify:表示把指向目标的关系的设置成 nil,但是不从 Core Data 中删除 Destination;
  • Cascade:表示在 Core Data 中连同对应的 Destination 一并删除,对于我们的例子来说,当删除一个视频系列时,理应删除所有和它对应的视频,因此我们把 episodes 关系的这个选项设置成了 Cascade;
  • No Action:表示什么都不做,绝大多数情况下,我们都不会使用这个设置;

当然,如果你还不是特别清楚这些选项的含义也完全没问题,只要对它们有个印象就好了,稍后,我们会专门用一节的内容通过代码详细和大家分享这些配置的效果。

另外,在 Episode 指回 Series 的关系里,我们可以把 Delete Rule 设置成 Nullify。表示删除视频后,Episode 到 Series 的引用就不存在了。完成这些设置之后,还是在菜单中选择 Editor / Create NSManagedObject Subclass...,这次,选中 Series 和 Episode 这两个 ENTITY:

这次,Xcode 会创建两个新文件:

  • Series+CoreDataClass.swift:是表示 Series ENITTY 的类,它和上一节创建的 Episode 是类似的:
@objc(Series)
public class Series: NSManagedObject {

}
  • Series+CoreDataProperties.swift:是 Series 中包含的属性,由于要表达一对多的关系,它看起来稍微复杂一些:
extension Series {
  @nonobjc public class func fetchRequest()
    -> NSFetchRequest<Series> {
    return NSFetchRequest<Series>(entityName: "Series")
  }

  @NSManaged public var id: Int64
  @NSManaged public var title: String?
  @NSManaged public var summary: String?
  @NSManaged public var episodes: NSSet?
}

// MARK: Generated accessors for episodes
extension Series {
  @objc(addEpisodesObject:)
  @NSManaged public func addToEpisodes(_ value: Episode)

  @objc(removeEpisodesObject:)
  @NSManaged public func removeFromEpisodes(_ value: Episode)

  @objc(addEpisodes:)
  @NSManaged public func addToEpisodes(_ values: NSSet)

  @objc(removeEpisodes:)
  @NSManaged public func removeFromEpisodes(_ values: NSSet)
}

可以看到,它包含了两个扩展。第一个扩展里,是表达一对多关系的属性,它是一个 NSSet 对象;第二个扩展里,是 Xcode 自动生成的,管理一对多关系的方法。

  • Episode+CoreDataProperties.swift:是上一节创建过的,由于我们修改了 Episode ENTITY 的定义,在这里,会更新出对应的属性:
extension Episode {

  /// The same as before...
  @NSManaged public var id: Int64
  @NSManaged public var seriesId: Int64
  @NSManaged public var ofSeries: Series?

}

可以看到,指回 Series 的关系,是通过是一个 Series? 类型的属性表示的。至此,Series 和 Episode 的关系,就设置好了。

History 和 Episode

接下来,我们创建表示用户浏览历史的 ENTITY:

其中:

  • id:表示数据库中视频历史记录的 ID;
  • episodeId:表示该记录对应的视频 ID;
  • progress:表示视频的观看进度;
  • updatedAt:表示生成记录的时间;

一个视频,应该只有一个最新的历史浏览记录;一个历史浏览记录,也只应该对应一个视频的信息,因此它们之间,是一对一的关系。为了表达这种关系,我们先给 History 创建一个指向 Episode 的关系。Delete Rule 设置成 Nullify,Type 设置成 To One:

再给 Episode 添加一个指回 History 的关系:

完成后,用和之前同样的方式给 History 生成 NSManagedObject 的派生类。在 History+CoreDataClass.swift 里,History 的定义是这样的:

@objc(History)
public class History: NSManagedObject {

}

在 History+CoreDataProperties.swift 里,它的属性是这样的:

extension History {
  @nonobjc public class func fetchRequest()
    -> NSFetchRequest<History> {
    return NSFetchRequest<History>(entityName: "History")
  }

  @NSManaged public var id: Int64
  @NSManaged public var progress: Double
  @NSManaged public var updatedAt: Date?
  @NSManaged public var episodeId: Int64
  @NSManaged public var ofEpisode: Episode?
}

可以看到,对于一对一的关系,生成的代码就简单多了,不会有一对多的那些用于批量管理关系的方法。在 History 里,指向 Episode 的关系是通过一个 Episode? 类型的属性表示的。而在更新后的 Episode+CoreDataProperties.swift 里,也会更新出来一个指回 History 的属性:

extension Episode {
  /// The same as before...
  @NSManaged public var history: History?
}

这样,History 和 Episode 的一对一关系,也就定义好了。

What's next?

以上,就是在 Core Data 中表达 ENTITY 关系的方法。创建好了 ENTITY 和 Relationships 之后,下一节,我们来看如何读写这种带有关系的 ENTITY。通过实际的代码,我们可以进一步加强对 Relationships 的理解。

所有订阅均支持 12 期免息分期

¥ 59

按月订阅

一个月,观看并下载所有视频内容。初来泊学,这可能是个最好的开始。

开始订阅

¥ 512

按年订阅

一年的时间,让我们一起疯狂地狩猎知识吧。比按月订阅优惠 28%

开始订阅

¥ 1280

泊学终身会员

永久观看和下载所有泊学网站视频,并赠送 100 元商店优惠券。

我要加入
如需帮助,欢迎通过以下方式联系我们