理解 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 自动删除。

相关推荐
若水无华2 天前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
Aress"2 天前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy2 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克2 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨2 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆2 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂3 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T3 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa
struggle20253 天前
适用于 iOS 的 开源Ultralytics YOLO:应用程序和 Swift 软件包,用于在您自己的 iOS 应用程序中运行 YOLO
yolo·ios·开源·app·swift
Unlimitedz3 天前
iOS视频编码详细步骤(视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265)
ios·音视频