ios SwiftData崩溃“非法尝试建立关系”

w46czmvw  于 5个月前  发布在  iOS
关注(0)|答案(2)|浏览(79)

我正在使用SwiftData和SwiftUI,xcode 15 beta 5。我试图在DB中建立实体之间的关系。这是一个用户,一本书,以及它们之间的所有权。

struct asdApp: App {

    let container = try! ModelContainer(for: [Ownership.self, User.self, Book.self])

    var body: some Scene {
        WindowGroup {
            ContentView()
                .task {
                    let jon = User()
                    container.mainContext.insert(jon)
                    let book = Book()
                    container.mainContext.insert(book)
                    let o = Ownership(keeper: jon, book: book) // <-- crash here!!
                    container.mainContext.insert(o)
                }
        }
        .modelContext(container.mainContext)
    }
}

字符串
下面是模型,这样你就有了完整的代码:

@Model
final class User {
    @Attribute(.unique) let id: UUID

    init(id: UUID = UUID()) {
        self.id = id
    }
}

@Model
final class Book {
    @Attribute(.unique) let id: UUID

    init(id: UUID = UUID()) {
        self.id = id
    }
}

@Model
final class Ownership {
    @Attribute(.unique) var id: UUID
    var keeper: User
    var book: Book

    init(id: UUID = UUID(), keeper: User, book: Book) {
        self.id = id
        self.keeper = keeper
        self.book = book
    }
}


以下是完整的错误:

Thread 1: "Illegal attempt to establish a relationship 'keeper' between objects in different contexts (source = <NSManagedObject: 0x60000212e5d0> (entity: Ownership; id: 0x600000243560 <x-coredata:///Ownership/tFADECEE1-82F7-4071-93B1-259354AE2E2A4>; data: {\n    book = nil;\n    id = \"6A63596F-66C2-45B3-91C0-FF24FF88B1B5\";\n    keeper = nil;\n}) , destination = <NSManagedObject: 0x60000214fed0> (entity: User; id: 0x600000299180 <x-coredata:///User/tFADECEE1-82F7-4071-93B1-259354AE2E2A2>; data: {\n    id = \"59B5122C-5CC6-4105-8F11-D48B6D46E63D\";\n}))"

第一个问题:只有一个上下文,那么为什么会崩溃?

如果我像这样更改顺序,则不会出现错误:

var body: some Scene {
    WindowGroup {
        ContentView()
            .task {
                let jon = User()
                let book = Book()
                let o = Ownership(keeper: jon, book: book)
                container.mainContext.insert(jon)
                container.mainContext.insert(book)
                container.mainContext.insert(o)
            }
    }
    .modelContext(container.mainContext)
}


但它并不正确,因为它创建了一本额外的书。下面是我如何发现的:

@main
struct asdApp: App {

    let container = try! ModelContainer(for: [Ownership.self, User.self, Book.self])

    var body: some Scene {
        WindowGroup {
            ContentView()
                .task {
                    clearAll(modelContainer: container)
                    let context = container.mainContext

                    let jon = User()
                    let book = Book()
                    let o = Ownership(keeper: jon, book: book)
                    context.insert(jon)
                    context.insert(book)
                    context.insert(o)

                    do {
                        for (index, user) in try  context.fetch(FetchDescriptor<User>()).enumerated() {
                            print("\(index) [USER]: id: \(user.id)")
                        }
                        
                        for (index, user) in try  context.fetch(FetchDescriptor<Book>()).enumerated() {
                            print("\(index) [BOOK]: id: \(user.id)")
                        }

                        for (index, user) in try  context.fetch(FetchDescriptor<Ownership>()).enumerated() {
                            print("\(index) [OWN]: id: \(user.id)")
                        }
                    } catch {
                        fatalError("Error while fetching all entities: \(error)")
                    }
                }
        }
        .modelContext(container.mainContext)
    }

    @MainActor
    func clearAll(modelContainer: ModelContainer) {
        do {
            (try modelContainer.mainContext.fetch(FetchDescriptor<User>()))
                .forEach {
                    modelContainer.mainContext.delete($0)
                }

            (try modelContainer.mainContext.fetch(FetchDescriptor<Book>()))
                .forEach {
                    modelContainer.mainContext.delete($0)
                }

            (try modelContainer.mainContext.fetch(FetchDescriptor<Ownership>()))
                .forEach {
                    modelContainer.mainContext.delete($0)
                }

            try modelContainer.mainContext.save()
        } catch {
            fatalError("Error while fetching all entities: \(error)")
        }
    }
}


下面是输出:

0 [USER]: id: 14B69B68-1123-4658-9399-7E60E1051A2D
0 [BOOK]: id: 3A52F512-E82F-4574-B2EF-A0B9258EA094
1 [BOOK]: id: 99FC93F5-B81F-4060-A549-869472E53D1D
0 [OWN]: id: 7F1D089A-AF00-4AB0-A17A-275E8448FC20

第二个问题:如果我只调用init一次(在调试期间它只出现一次),为什么有两本书,它们是如何得到不同的id的?我想要一本书,一个用户和一个关系。

实际的例子更复杂,这是我能达到的最低限度。

mfpqipee

mfpqipee1#

在App.swift文件的WindowGroup后面添加这样的模型容器。modelContainer(for:[Ownership.self,User.self,Book.self])//SwiftData会推断出上面数组中的项的依赖关系,所以不需要全部添加,但如果需要可以
将SwiftData假设的模型上下文传递到需要它的文件中:@Environment(.modelContext)var modelContext然后像这样添加:modelContext.insert(objectNameHere)
book存在两次,因为你在类中初始化它,然后在所有权中再次调用它。

vybvopom

vybvopom2#

我找到的解决方案是在Ownership.init()之外初始化keeperuser变量属性
我是说

@Model
final class Ownership {
    @Attribute(.unique) var id: UUID
    var keeper: User?
    var book: Book?

    init(id: UUID = UUID()) {
        self.id = id
    }
    func makeConstraint(keeper: User, book: Book) {
        self.keeper = keeper
        self.book = book
    }
}

字符串

相关问题