SwiftUI+Realm使用ConfirmationDialog删除时崩溃问题求助
问题:SwiftUI结合Realm使用ConfirmationDialog删除数据时崩溃
Sheet中执行删除操作正常,但通过ConfirmationDialog删除数据时,数据库中数据已成功删除,但应用会崩溃。以下是最小可复现示例:
入口代码
import SwiftUI @main struct ForTestApp: App { @StateObject private var store = SpaceStore() var body: some Scene { WindowGroup { ContentView() .environmentObject(store) } } }
ContentView代码
import SwiftUI import RealmSwift struct ContentView: View { @EnvironmentObject var store: SpaceStore var body: some View { Button("Add") { for id in 0...5 { SpaceStore.add(by: "tag: "+id.formatted()) } } Divider() if let spaces = store.spaces { ForEach(spaces) { space in RowView(space: space) } } } } #Preview { ContentView() .environmentObject(SpaceStore()) }
RowView代码
import SwiftUI struct RowView: View { let space: Space @State private var ifShowDialog = false @State private var ifShowSheet = false var body: some View { HStack { Text(space.name) Button("sheet") { ifShowSheet.toggle() } .sheet(isPresented: $ifShowSheet) { Button("Delete") { SpaceStore.delete(space) } Button("ConfirmationDialogDelete") { ifShowDialog.toggle() } .confirmationDialog("", isPresented: $ifShowDialog) { Button("Delete", role: .destructive) { SpaceStore.delete(space) } } } } } }
Model代码
import RealmSwift class Space: Object, Identifiable { @Persisted(primaryKey: true) var id: ObjectId @Persisted var name: String = "" convenience init(name: String) { self.init() self.name = name } }
ViewModel代码
import Foundation import RealmSwift class SpaceStore: ObservableObject { @Published var spaces: Results<Space>? static let realm = try! Realm() private var realmToken: NotificationToken? init() { let results = SpaceStore.realm.objects(Space.self) realmToken = results.observe { _ in self.spaces = results } } static func add(by name: String) { let space = Space(name: name) try! SpaceStore.realm.write { SpaceStore.realm.add(space) } } static func delete(_ space: Space) { try! SpaceStore.realm.write { SpaceStore.realm.delete(space) } } }
崩溃原因
删除Realm对象后,该对象会被标记为isInvalidated = true,此时任何对该对象属性的访问都会触发崩溃。在ConfirmationDialog的场景中:
- 删除操作完成后,Realm的实时Results会更新,ContentView的ForEach会自动移除对应的RowView
- 但RowView内部的
ifShowDialog和ifShowSheet状态仍为true,导致ConfirmationDialog和Sheet的视图仍试图引用已失效的space对象 - 而Sheet直接删除的场景中,RowView被移除后,Sheet的
isPresented绑定的状态随之销毁,Sheet自动关闭,不会触发无效对象访问
解决方案
方案1:删除后立即关闭弹窗和Sheet
修改RowView中的删除逻辑,在删除后手动重置状态,切断对失效对象的引用:
// RowView中ConfirmationDialog的Delete按钮修改为: Button("Delete", role: .destructive) { SpaceStore.delete(space) // 关闭弹窗和Sheet ifShowDialog = false ifShowSheet = false } // Sheet中的Delete按钮也可以同步加上关闭逻辑: Button("Delete") { SpaceStore.delete(space) ifShowSheet = false }
方案2:通过对象ID执行删除操作(更安全)
避免直接持有Realm对象,改用对象ID来执行删除,即使对象被删除也不会触发无效引用:
- 修改SpaceStore的delete方法:
static func delete(by id: ObjectId) { guard let space = realm.object(ofType: Space.self, forPrimaryKey: id) else { return } try! realm.write { realm.delete(space) } }
- 修改RowView中的调用:
// Sheet中的Delete按钮 Button("Delete") { SpaceStore.delete(by: space.id) ifShowSheet = false } // ConfirmationDialog中的Delete按钮 Button("Delete", role: .destructive) { SpaceStore.delete(by: space.id) ifShowDialog = false ifShowSheet = false }
这两种方案都能解决崩溃问题,方案2更符合Realm的最佳实践,避免持有可能失效的对象引用。
内容的提问来源于stack exchange,提问作者hi-mango
相关产品推荐
相关产品推荐

