🔬
【SwiftData】DeleteRule 観測隊 (.cascade .nullify .deny .noAction)
1:1
片方向 A->B
サンプルコード
@Model
final class TestA {
var name: String
@Relationship(deleteRule: .cascade) var testB: TestB?
//以下略
@Model
final class TestB {
var name: String
//以下略
A内のBにはBインスタンスを登録する。
| A内の Bの宣言 |
B内の Aの宣言 |
Aを削除したとき | Bを削除したとき |
|---|---|---|---|
| .cascade | - | A消える / B消える 問題なし |
A消えない / B消える A内のBにアクセスするとEXC_BREAKPOINT |
| .nullify | - | A消える B消えない 問題なし |
同上 |
| .deny | - | A消える B消えない エラー大量、実行は継続 |
同上 |
| .noAction | - | A消える B消えない 問題なし |
同上 |
双方向 C<->D
サンプルコード
@Model
final class TestC {
var name: String
@Relationship(deleteRule: .cascade) var testD: TestD?
//以下略
@Model
final class TestD {
var name: String
@Relationship(deleteRule: .cascade) var testC: TestC?
//以下略
C内のDにはDインスタンスを登録する。
| C内の Dの宣言 |
D内の Cの宣言 |
Cを削除したとき | Dを削除したとき |
|---|---|---|---|
| .cascade | .cascade | 両方消える 問題なし |
両方消える 問題なし |
| .cascade | .nullify | 同上 | C消えない D消える 問題なし |
| .cascade | .deny | 同上 | EXC_BAD_ACCESS |
| .cascade | .noAction | 同上 | EXC_BAD_ACCESS |
| .nullify | .nullify | C消える D消えない 問題なし |
C消えない D消える 問題なし |
| .nullify | .deny | 同上 | EXC_BAD_ACCESS |
| .nullify | .noAction | 同上 | EXC_BAD_ACCESS |
| .deny | .deny | EXC_BAD_ACCESS | EXC_BAD_ACCESS |
| .deny | .noAction | EXC_BAD_ACCESS | EXC_BAD_ACCESS |
| .noAction | .noAction | EXC_BAD_ACCESS | EXC_BAD_ACCESS |
1:N
片方向 E->F
サンプルコード
@Model
final class TestE {
var name: String
@Relationship(deleteRule: .noAction) var testF: [TestF] = []
//以下略
@Model
final class TestF {
var name: String
//以下略
E内のFに2つ入れる。
| E内の Fの宣言 |
F内の Eの宣言 |
Eを削除したとき | Fを削除したとき |
|---|---|---|---|
| .cascade | - | E消える F消える 問題なし |
F消える EのFにアクセスするとEXC_BREAKPOINT |
| .nullify | - | E消える F消えない 問題なし |
同上 |
| .deny | - | E消える F消えない エラー大量 |
同上 |
| .noAction | - | E消える F消えない 問題なし |
同上 |
片方向 G<-H
サンプルコード
@Model
final class TestG {
var name: String
//以下略
@Model
final class TestH {
var name: String
@Relationship(deleteRule: .cascade) var testG: TestG?
//以下略
複数のHから同じGに接続。
| G内の Hの宣言 |
H内の Gの宣言 |
Gを削除したとき | Hを削除したとき |
|---|---|---|---|
| - | .cascade | G消える HのGにアクセスするとEXC_BREAKPOINT |
G消える H消える もうひとつのHのGにアクセスするとEXC_BREAKPOINT |
| - | .nullify | 同上 | G消えない H消える 問題なし |
| - | .deny | 同上 | G消えない H消える エラー大量、実行は継続 |
| - | .noAction | 同上 | G消えない H消える 問題なし |
双方向 I<->J
サンプルコード
@Model
final class TestI {
var name: String
@Relationship(deleteRule: .noAction) var testJ: [TestJ] = []
//以下略
@Model
final class TestJ {
var name: String
@Relationship(deleteRule: .noAction) var testI: TestI?
//以下略
複数のJから同じIに接続。
| I内の Jの宣言 |
J内の Iの宣言 |
Iを削除したとき | Jを削除したとき |
|---|---|---|---|
| .cascade | .cascade | I消える J全て消える 問題なし |
J消える I消える Iに関連する他のJ消える 問題なし |
| .cascade | .nullify | 同上 | 消したJだけ消える I消えない Iに関連する他のJ消えない 問題なし |
| .cascade | .deny | 同上 | 消したJだけ消える I消えない Iに関連する他のJ消えない エラー大量、実行は継続 |
| .cascade | .noAction | 同上 | 消したJだけ消える I消えない Iに関連する他のJ消えない IのJにアクセスするとEXC_BREAKPOINT |
| .nullify | .nullify | I消える J消えない 問題なし |
消したJだけ消える I消えない Iに関連する他のJ消えない 問題なし |
| .nullify | .deny | 同上 | 消したJだけ消える I消えない Iに関連する他のJ消えない エラー大量、実行は継続 |
| .nullify | .noAction | 同上 | 消したJだけ消える I消えない Iに関連する他のJ消えない IのJにアクセスするとEXC_BREAKPOINT |
| .deny | .deny | I消える J消えない エラー大量、実行は継続 |
消したJだけ消える I消えない Iに関連する他のJ消えない エラー大量、実行は継続 |
| .deny | .noAction | 同上 | 消したJだけ消える I消えない Iに関連する他のJ消えない IのJにアクセスするとEXC_BREAKPOINT |
| .noAction | .noAction | I消える J消えない JのIにアクセスするとEXC_BREAKPOINT |
消したJだけ消える I消えない Iに関連する他のJ消えない IのJにアクセスするとEXC_BREAKPOINT |
N:N
片方向参照 K->L
サンプルコード
@Model
final class TestK {
var name: String
@Relationship(deleteRule: .cascade) var testL: [TestL] = []
//以下略
@Model
final class TestL {
var name: String
//以下略
K内のLに2つ入れる。
| K内の Lの宣言 |
L内の Kの宣言 |
Kを削除したとき | Lを削除したとき |
|---|---|---|---|
| .cascade | - | K消える L消える 問題なし |
L消える KのLにアクセスするとEXC_BREAKPOINT |
| .nullify | - | K消える L消えない 問題なし |
同上 |
| .deny | - | K消える L消えない エラー大量 |
同上 |
| .noAction | - | K消える L消えない 問題なし |
同上 |
双方向参照 inverse設定なし O<->P
サンプルコード
@Model
final class TestO {
var name: String
@Relationship(deleteRule: .cascade) var testP: [TestP] = []
//以下略
@Model
final class TestP {
var name: String
@Relationship(deleteRule: .cascade) var testO: [TestO] = []
//以下略
2つのOと2つのPがすべて組み合わさっている。
| O内の Pの宣言 |
P内の Oの宣言 |
Oを削除したとき | Pを削除したとき |
|---|---|---|---|
| .cascade | .cascade | 連鎖すべて消える | 連鎖すべて消える |
| .cascade | .nullify | 消したO消える 消したOと繋がっているPは消える 消えたPと繋がっている他のOは消えない 残ったOのPにアクセスするとEXC_BREAKPOINT |
消したP消える 消したPと繋がっているOは消えない Oの消えたPにアクセスするとEXC_BREAKPOINT |
| .cascade | .deny | 消したO消える 消したOと繋がっているPは消える 消えたPと繋がっている他のOは消えない エラー大量、実行継続 |
消したP消える 消したPと繋がっているOは消えない エラー大量、実行継続 |
| .cascade | .noAction | 消したO消える 消したOと繋がっているPは消える 消えたPと繋がっている他のOは消えない 残ったOのPにアクセスするとEXC_BREAKPOINT |
消したP消える 消したPと繋がっているOは消えない Oの消したPにアクセスするとEXC_BREAKPOINT |
| .nullify | .nullify | 消したO消える 消したOと繋がっているPは消えない Pの消したOにアクセスするとEXC_BREAKPOINT |
消したP消える 消したPと繋がっているOは消えない Oの消したPにアクセスするとEXC_BREAKPOINT |
| .nullify | .deny | 同上 | 消したP消える 消したPと繋がっているOは消えない エラー大量、実行継続 |
| .nullify | .noAction | 同上 | 消したP消える 消したPと繋がっているOは消えない Oの消したPにアクセスするとEXC_BREAKPOINT |
| .deny | .deny | 消したO消える 消したOと繋がっているPは消えない エラー大量、実行継続 |
消したP消える 消したPと繋がっているOは消えない エラー大量、実行継続 |
| .deny | .noAction | 同上 | 消したP消える 消したPと繋がっているOは消えない Oの消したPにアクセスするとEXC_BREAKPOINT |
| .noAction | .noAction | 消したO消える 消したOと繋がっているPは消えない Pの消したOにアクセスするとEXC_BREAKPOINT |
消したP消える 消したPと繋がっているOは消えない Oの消したPにアクセスするとEXC_BREAKPOINT |
.cascade - .cascade を除くといいことが何もないw
双方向参照 inverse設定付き Q<->R
サンプルコード
@Model
final class TestQ {
var name: String
@Relationship(deleteRule: .cascade, inverse: \TestR.testQ) var testR: [TestR] = []
//以下略
@Model
final class TestR {
var name: String
@Relationship(deleteRule: .cascade) var testQ: [TestQ] = []
//以下略
2つのQと2つのRがすべて組み合わさっている。
| Q内の Rの宣言 |
R内の Qの宣言 |
Qを削除したとき | Rを削除したとき |
|---|---|---|---|
| .cascade | .cascade | 連鎖すべて消える | 連鎖すべて消える |
| .cascade | .nullify | 消したQ消える 消したQと繋がっているRは消える 消えたRと繋がっている他のQは消えない 問題なし |
【R:.nullify】 消したR消える 消したRと繋がっているQは消えない 問題なし |
| .cascade | .deny | 消したQ消える 消したQと繋がっているRは消える 消えたRと繋がっている他のQは消えない エラー大量、実行継続 |
【R:.deny】 消したR消える 消したRと繋がっているQは消えない エラー大量、実行継続 |
| .cascade | .noAction | 消したQ消える 消したQと繋がっているRは消える 消えたRと繋がっている他のQは消えない 残ったQのRにアクセスするとEXC_BREAKPOINT |
【R:.noAction】 消したR消える 消したRと繋がっているQは消えない Qの消したRにアクセスするとEXC_BREAKPOINT |
| .nullify | .cascade | 消したQ消える 消したQと繋がっているRは消えない 問題なし |
消したR消える 消したRと繋がっているQは消える 消えたQと繋がっている他のRは消えない 問題なし |
| .nullify | .nullify | 同上 | 【R:.nullify】と同じ |
| .nullify | .deny | 同上 | 【R:.deny】と同じ |
| .nullify | .noAction | 同上 | 【R:.noAction】と同じ |
| .deny | .cascade | 消したQ消える 消したQと繋がっているRは消えない エラー大量、実行継続 |
消したR消える 消したRと繋がっているQは消える 消えたQと繋がっている他のRは消えない エラー大量、実行継続 |
| .deny | .nullify | 同上 | 【R:.nullify】と同じ |
| .deny | .deny | 同上 | 【R:.deny】と同じ |
| .deny | .noAction | 同上 | 【R:.noAction】と同じ |
| .noAction | .cascade | 消したQ消える 消したQと繋がっているRは消えない Rの消したQにアクセスするとEXC_BREAKPOINT |
消したR消える 消したRと繋がっているQは消える 消えたQと繋がっている他のRは消えない 残ったRのQにアクセスするとEXC_BREAKPOINT |
| .noAction | .nullify | 同上 | 【R:.nullify】と同じ |
| .noAction | .deny | 同上 | 【R:.deny】と同じ |
| .noAction | .noAction | 同上 | 【R:.noAction】と同じ |
接続なしの状態について
それぞれのパターンで接続なし、つまり参照を表す変数が
- nil
- 配列が空
であれば、削除しても、削除したものだけが消えて、他に影響をあたえず問題ない。
まとめ、考察
denyが指定してあるものはUI上で消えてもデータは残っているかも
N対Nの両nullifyにするときはinverseの設定が必須
配列の要素を削除する時
ここまではインスタンスを丸ごと削除するときの挙動を見てきたが、N:1やN:Nのときは配列で他の要素を保持/削除することもある。配列の要素を削除するときの挙動は
.nullify は想定通りの挙動をするが、.cascade は .nullify の挙動をするようである。
他の記事
主に配列の要素を削除するときなど図入りで書きました。
Discussion