理解 Core Data 的 Relationship

前言

关系(Relationship)是表示一个实体(Entity)如何去影响另一个实体的,所以它至少描述两个实体。配置关系的话,我们需要提供以下几项内容:

  • 名字
  • 目标实体
  • 删除规则
  • 基数类型(一对一、一对多、多对多)
  • 是否将关系保存在存储中
  • 保存时是否允许 optional
  • 每个关系必须有一个逆关系

下面我们来看一下如何使用关系。

使用关系

假设我们有一个需求,需要将患者的电子病历存储到本地。我们首先定义三个实体:User(患者)、 Record(病历)和 Report (报告图片)。三者的关系如下:

  • 同一患者可以有多份病历,所以 User 对 Record 的关系是一对多的,关系的名字为 records,Xcode 示例图如下:
  • 同一份病历只能对应一个患者,所以 Record 对 User 的关系是一对一的。关系的名字为 user,Xcode 示例图如下:
  • 同一份病历可以有多张报告结果,所以 Record 对 Report 是一对多的。关系的名字为 reports,Xcode 示例图如下:
  • 同一份报告只能对应一份病历,所以 Report 对 Record 是一对一的。关系的名字为 record,Xcode 示例图如下:

在写示例代码之前,我们先详细介绍一下关系 inspector 里各个参数的含义,官网 inspector 的示例图如下:

  • Transient:勾选该项,则意味着关系不会保存到持久存储中。因此,Transient 对于临时存储计算值或派生值是非常有用。Core Data 会跟踪临时属性值的更改以用于进行撤销操作。
  • Optional:勾选该项,则意味着关系会保存到持久存储中且关系必须指向目标类型的一个或者多个实例。创建关系默认勾选该项。
  • Destination:每个关系都从源实体(添加关系的实体)指向目标实体。目的实体是影响源类型的相关类型,并且受源类型的影响。
  • Inverse:当源或目标类型的实例发生更改时,反向关系使 Core Data 能够在两个方向上传播更改。每一种关系都必须有一个逆关系。
  • Delete Rule:Core Data 有以下四种删除规则:
    • No Action:仅删除源实体实例对象,对目标实体对象不做任何处理。这也意味着不会享受 Core Data 关系带来的自动管理的便利。需要开发者自己去处理相应的删除逻辑。会保留实例对象的引用,可能会造成空指针问题,使用时需仔细。
    • Nullify:删除源实体实例对象,并且会删除该对象上的关系。但不会删除关联的目标实体对象。比如上述的关系患者和该患者的多份病历,删除一份病历并不会导致该患者信息被删除。
    • Cascade:删除删除源实体实例对象,并且会删除关联的目标实体对象。比如上述的关系患者和该患者的病历,删除患者那么该患者的病历也没有存在的必要了。
    • Deny:若关联的对象还存在,那么会阻止删除操作,它用于确保不会因删除一个对象而产生空指针的问题。
  • Cardinality Type:一对一、一对多。一对多可指定数量的上限和下限。
  • Index in Spotlight:将该字段包含在 Spotlight 索引中。

概念介绍完,下面看一下示例代码。

代码示例

ini 复制代码
func setupData() {
    let context = persistentContainer.viewContext
    let entity = NSEntityDescription.entity(forEntityName: userEntityName, in: context)
    
    let user = User(entity: entity!, insertInto: context)
    user.name = "name"
    
    let recordEntity = NSEntityDescription.entity(forEntityName: recordEntityName, in: context)
    let record = Record(entity: recordEntity!, insertInto: context)
    record.hospital = "hospital"
    record.date = Date()
    record.recordID = UUID()
    record.disease = "disease"
    record.medicalHistory = "medicalHistory"
    
    let record2 = Record(entity: recordEntity!, insertInto: context)
    record2.hospital = "hospital2"
    record2.date = Date()
    record2.recordID = UUID()
    record2.disease = "disease2"
    record2.medicalHistory = "medicalHistory2"
    
    let reportEntity = NSEntityDescription.entity(forEntityName: reportEntityName, in: context)
    let report = Report(entity: reportEntity!, insertInto: context)
    report.imageData = Data()
    record.addToReports(report)
    
    user.addToRecords([record, record2])
    save()
}

代码说明: 首先在 setupData() 函数里面创建需要持久化的数据。然后根据逻辑将初始化的对象添加到相应的实体对象上面。需要说明的是:addToReports()/addToRecords() 都是 XCode 根据关系自动生成的代码。如何创建自动生成的代码可以参考这里

将上述数据创建好之后,如果删除了 user ,根据关系,它底下关联的 recordrecord2report 也会被 Core Data 自动删除。

相关推荐
2601_9588151618 小时前
面向视疲劳缓解的手机贴膜光学系统设计:scinique®双护协同技术的工程实现
ios·智能手机·iphone·圆偏振光护眼·磁控溅射ar抗反射·iphone护眼膜
吠品20 小时前
Go赋能:HTTP大文件秒传与断点续接
ios·iphone·xcode
唐诺1 天前
【无标题】
ios·属性包装器·wrappers
测试员周周2 天前
【Appium 系列】第14节-断言与验证 — Validator 的设计
android·人工智能·python·功能测试·ios·单元测试·appium
2501_916008892 天前
Mac 上生成 AppStoreInfo.plist 文件,App Store 上架
android·macos·ios·小程序·uni-app·iphone·webview
人月神话-Lee2 天前
【图像处理】高斯模糊——最优雅的模糊算法
图像处理·人工智能·算法·ios·ai编程·swift
@大迁世界2 天前
iPhone 18e,可能不再“低一档”
ios·iphone
Daniel_Coder2 天前
iOS Widget 开发-20:从旧版 API 迁移到 iOS 17+ 现代 Widget
ios·swift·widget·widgetcenter
Daniel_Coder2 天前
iOS Widget 开发-19:Widget 调试与单元测试
ios·单元测试·swift·widget·widgetcenter