理解 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 ,根据关系,它底下关联的 record、record2 和 report 也会被 Core Data 自动删除。

相关推荐
黑科技iOS上架12 小时前
App Store5.6隐藏功能究竟怎么排查?
ios·审核
Yannik15 小时前
iOS Swift Package Manager(SPM)私有组件完整实操
ios·swift
随遇丿而安15 小时前
第 4 周:28KB 的 PNG 占了 8MB 内存,因为图片内存看尺寸不看文件大小
ios
leizhen1984080916 小时前
Vibe Coding:门槛降了,但判断力成了新的门槛
人工智能·安全·ios
我就是不信20 小时前
Swift 方法派发机制详解
开发语言·ios·swift
AIsoft_86891 天前
科会通录音工具倍速回放怎么切换速度
ios·智能手机
老李IT笔记1 天前
设备显示在线但指令没回执:三类静默失效的排查顺序
ios·iphone
芯械研究所1 天前
她宁、益博思、倍思、绿联哪款ipad电容笔好?四款电容笔款式实测解读
ios·ipad·电容笔·电容笔推荐·电容笔测评
Yannik2 天前
iOS CocoaPods私有库完整搭建+踩坑实战总结
ios·cocoapods