SwiftData 中的插入顺序对依赖实例和父实例有何影响?

问题描述 投票:0回答:1

在 SwiftData 中,依赖实例和父实例的插入顺序是什么?在下面的代码片段中,当依赖实例插入到父实例之前时,它会起作用。但是,当顺序颠倒时,它会失败。为什么会这样?


import Foundation
import SwiftData
import SwiftUI

struct DebugView: View {
  @Query var parent: [ParentClass]
  @Query var dept: [DependentClass]
  var body: some View {
    Text("parent cnt:\(parent.count), dept cnt:\(dept.count)")
  }
}

@Model
class ParentClass {
  @Relationship(deleteRule: .cascade)
  var deps: DependentClass

  init(deps: DependentClass) {
    self.deps = deps
  }
}

@Model
class DependentClass {
  var id: String
  init(id: String = UUID().uuidString) {
    self.id = id
  }
}

#Preview {
  do {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try ModelContainer(for: ParentClass.self, configurations: config)
    let context = ModelContext(container)

    var dept = DependentClass()
    var parentClass = ParentClass(deps: dept)

    /* ok to insert dept before parent. View shows: "parent cnt:1, dept cnt:1" */
    context.insert(dept) 
    context.insert(parentClass)

    /* ok to insert only parent, swiftData will insert dept too. View shows: "parent cnt:1, dept cnt:1" */
    // context.insert(parentClass) 

    /* failed to insert dept after parent, error list below */
    // context.insert(parentClass)
    // context.insert(dept) 

    return DebugView()
      .modelContainer(container)
  } catch {
    return Text("Failed to create container: \(error.localizedDescription)")
  }
}

错误日志插入父级之后的部门:

Date/Time:           2024-01-13 14:10:10.3694 +0800
Launch Time:         2024-01-13 14:10:09.9932 +0800
OS Version:          macOS 14.1.1 (23B81)
Release Type:        User
Report Version:      104

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x00000001929f3938
Termination Reason: SIGNAL 5 Trace/BPT trap: 5
Terminating Process: exc handler [51868]

Triggered by Thread:  0

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libswiftCore.dylib                     0x1929f3938 _assertionFailure(_:_:file:line:flags:) + 248
1   SwiftData                              0x1c42fc454 0x1c42d1000 + 177236
2   SwiftData                              0x1c4315aac 0x1c42d1000 + 281260
3   SwiftData                              0x1c42fd4e0 0x1c42d1000 + 181472
4   DebugView.1.preview-thunk.dylib        0x10161ae10 closure #1 in static $s39gogodict_PreviewReplacement_DebugView_133_20B25209EACDFA98A88DFB5B90B26E4CLl0B0fMf_15PreviewRegistryfMu_.makePreview() + 840 (@__swiftmacro_39gogodict_PreviewReplacement_DebugView_133_20B25209EACDFA98A88DFB5B90B26E4CLl0B0fMf_.swift:18)
5   PreviewsInjection                      0x1d6aaa0ec 0x1d6a6f000 + 241900
6   PreviewsInjection                      0x1d6aab060 0x1d6a6f000 + 245856
7   libswift_Concurrency.dylib             0x1e450b738 static MainActor.assumeIsolated<A>(_:file:line:) + 144
8   PreviewsInjection                      0x1d6aa9e48 0x1d6a6f000 + 241224
9   PreviewsInjection                      0x1d6aadfb8 0x1d6a6f000 + 257976
10  PreviewsInjection                      0x1d6aaea88 0x1d6a6f000 + 260744
11  PreviewsInjection                      0x1d6a9c89c 0x1d6a6f000 + 186524
12  PreviewsInjection                      0x1d6a9f5c8 0x1d6a6f000 + 198088
13  PreviewsInjection                      0x1d6a87c90 0x1d6a6f000 + 101520
14  PreviewsInjection                      0x1d6a88128 0x1d6a6f000 + 102696
15  PreviewsInjection                      0x1d6aa0fa0 0x1d6a6f000 + 204704
16  PreviewsInjection                      0x1d6a777b8 0x1d6a6f000 + 34744
17  PreviewsInjection                      0x1d6a77004 0x1d6a6f000 + 32772
18  PreviewsFoundation                     0x1d69b4630 0x1d691c000 + 624176
19  libdispatch.dylib                      0x18016b4f4 _dispatch_call_block_and_release + 24
20  libdispatch.dylib                      0x18016cd3c _dispatch_client_callout + 16
21  libdispatch.dylib                      0x18017bb24 _dispatch_main_queue_drain + 1272
22  libdispatch.dylib                      0x18017b61c _dispatch_main_queue_callback_4CF + 40
23  CoreFoundation                         0x1803f1a30 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
24  CoreFoundation                         0x1803ec148 __CFRunLoopRun + 1936
25  CoreFoundation                         0x1803eb5a4 CFRunLoopRunSpecific + 572
26  GraphicsServices                       0x18e9fbae4 GSEventRunModal + 160
27  UIKitCore                              0x1852f02e4 -[UIApplication _run] + 868
28  UIKitCore                              0x1852f3f5c UIApplicationMain + 124
29  SwiftUI                                0x1c51fc1b0 0x1c4371000 + 15249840
30  SwiftUI                                0x1c51fc050 0x1c4371000 + 15249488
31  SwiftUI                                0x1c4f02fa4 0x1c4371000 + 12132260
32  gogodict                               0x1009f03b0 static GOGODictApp.$main() + 40
33  gogodict                               0x1009f0474 main + 12 (GOGODictApp.swift:12)
34  dyld_sim                               0x100bfd544 start_sim + 20
35  dyld                                   0x100ce60e0 start + 2360

对于像我这样的新手来说,这似乎很令人困惑。有没有解释引擎底层逻辑的文档或者文章?

swift swiftui swift-data
1个回答
0
投票

当您为对象分配关系属性时,SwiftData 将根据需要为您处理插入当前 ModelContext 实例的操作。

所以当你有像 ParentClass 这样的 init 时

init(deps: DependentClass) {
  self.deps = deps
}

然后 DependentClass 对象(从这里开始我将它们称为父对象和依赖对象)将被插入到与父对象相同的上下文中(如果已插入)。

问题是你(目前)永远不能将同一个对象两次插入到同一个上下文中,否则你会崩溃。希望这个问题很快就能得到解决,这样 SwiftData 就会忽略第二次插入,或者更好的是我们会得到一个编译时错误,但我不确定这是否可能。

所以要了解问题中的场景:

先插入依赖对象

context.insert(dept) 
context.insert(parentClass)

这里,当执行父 init 时,依赖项已经插入到上下文中,并且 SwiftData 正确地确认了这一点,并且不会尝试插入依赖项。这确实不足为奇,因为这是一个非常常见的用例,关系的一侧已经存在于上下文中。

切勿插入依赖对象

context.insert(parentClass) 

这已经在前面描述过,当插入父级时,SwiftData 会处理从属级的插入。 请注意,如果您有一对多关系,在连接两个对象之前必须先将对象插入到上下文中,这也可能会导致一些问题

最后插入依赖对象

context.insert(parentClass) 
context.insert(dept) 

这是崩溃场景,因为 SwiftData 已经将依赖对象插入到上下文中,最后一次插入将导致崩溃。


总而言之,顺序很重要,特别是对于多对多关系,但导致崩溃的原因是一个对象被多次插入到模型上下文中。

© www.soinside.com 2019 - 2024. All rights reserved.