在 SwiftData 中,模型之间的关系可以推断出来,,也可以使用 @Relationship 宏显式的表达出来。一般来说,当你想要一个非默认配置时,你需要 @Relationship;但在很多时候你可以忽略它。
例如,我们有一个 Bank 类用来表示银行,有一个 Customer 类用来表示顾客,示例代码如下:
class Bank {
var name: String
var customers: [Customer]
init(name: String, customers: [Customer]) {
self.name = name
self.customers = customers
}
}
@Model
class Customer {
var name: String
var bank: Bank
init(name: String, bank: Bank) {
self.name = name
self.bank = bank
}
}
根据上面的代码,SwiftData 可以推断出以下的模型关系:
- 每个银行可以有很多顾客(银行对顾客是一对多的)。
- 每个顾客必须恰好属于某一个银行(顾客对银行是一对一的)。
但是,现在这两种关系是分开的:如果我们创建一个顾客并设置其银行属性,SwiftData 不会将该顾客添加到该银行的 customers 数组中,因为它不会自动推断出这种关系是双向的。
如果我们对 Customer 模型进行一个小的更改,我们才会得到一个属性关系推理:
@Model
class Customer {
var name: String
var bank: Bank?
init(name: String, bank: Bank?) {
self.name = name
self.bank = bank
}
}
唯一的变化是我们将银行标记为可选的,这意味着它可以是空的。这是出于安全原因,要理解原因,请考虑以下几点:
- 如果我们在顾客和银行之间有关系,那么设置顾客的 bank 属性应该将其从银行的 customers 中添加或删除。
- 同样,从银行的 customers 中增加或删除顾客也应该调整顾客的 bank 属性。
- 那么,如果你把一个顾客从一所银行移走,而没有把他添加到另一所银行,会发生什么呢?
当我们将bank 定义为非可选参数时,那顾客就必须属于某个银行。如果我们试图打破这个规则,SwiftData 就会在我们的应用中引发崩溃,因为我们把它设置为无效状态。
所以,SwiftData 默认采用了唯一安全的方法:它只会在安全的情况下推断关系。即当它不会因为我们改变了数组而无意中触发崩溃的时候。
另一方面,一旦我们将 bank 属性设置为可选属性,这个危险就消失了:从 customers 数组中删除一个 Customer 只会将他们的 bank 属性设置为 nil,所以没有崩溃风险。
这里的规则很简单:如果一个关系可以安全地推断出来,SwiftData 就可以自动的推断出来。
但很多时候,这还不够,我们可以在两个模型之一上使用 @Relationship 宏创建显式关系。例如,我们可以改变Customer 类,将它的 bank 属性修改如下:
@Model
class Customer {
var name: String
@Relationship(inverse: \Bank.customers) var bank: Bank
init(name: String, bank: Bank) {
self.name = name
self.bank = bank
}
}
可以看到上面的代码将 bank 设置成了非可选参数。如果我们显示的使用了 @Relationship,那参数可以是可选的,也可以是非可选的。这里不存在二义性了,因为你明确告诉了 SwiftData 你预期的关系是什么。