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

相关推荐
专业开发者9 小时前
调试 iOS 蓝牙应用的新方法
物联网·macos·ios·cocoa
tangbin58308514 小时前
iOS Swift 可选值(Optional)详解
前端·ios
卷心菜加农炮1 天前
基于Python的FastAPI后端开发框架如何使用PyInstaller 进行打包与部署
ios
北极象2 天前
千问大模型接入示例
ios·iphone·qwen
ipad协议开发2 天前
企业微信 iPad 协议应用机器人开发
ios·企业微信·ipad
QuantumLeap丶2 天前
《Flutter全栈开发实战指南:从零到高级》- 26 -持续集成与部署
android·flutter·ios
2501_915918412 天前
TCP 抓包分析在复杂网络问题中的作用,从连接和数据流层面理解系统异常行为
网络·网络协议·tcp/ip·ios·小程序·uni-app·iphone
二流小码农2 天前
鸿蒙开发:个人开发者如何使用华为账号登录
android·ios·harmonyos
wvy3 天前
Xcode 26还没有适配SceneDelegate的app建议尽早适配
ios
游戏开发爱好者83 天前
苹果 App 上架流程,结合 Xcode、CI 等常见工具
macos·ios·ci/cd·小程序·uni-app·iphone·xcode