【问题标题】:Deleting from RealmSwift.List<Object> raises out of bounds exception从 RealmSwift.List<Object> 中删除会引发越界异常
【发布时间】:2020-09-09 20:04:51
【问题描述】:

我一直在尝试遵循 Realm Swift 团队提供的 ListSwiftUI 示例。它对我来说一直很好,但是我遇到了一个问题,当我尝试从 RealmSwift.List&lt;Object&gt; 中删除时,应用程序崩溃并出现异常 "Terminating app due to uncaught exception 'RLMException', reason: 'Index 5 is out of bounds (must be less than 5).'"

型号:

final class Todo: Object, ObjectKeyIdentifiable {
    @objc dynamic var id: String = UUID().uuidString
    @objc dynamic var name: String = ""
    @objc dynamic var createdAt: Date = Date()
    
    override class func primaryKey() -> String? {
        "id"
    }
}

final class Todos: Object {
    @objc dynamic var id: Int = 0
    
    let todos = List<Todo>()
    
    override class func primaryKey() -> String? {
        "id"
    }
}

SceneDelegate.swift

let realm = try! Realm()
var todos = realm.object(ofType: Todos.self, forPrimaryKey: 0)
if todos == nil {
    todos = try! realm.write { realm.create(Todos.self, value: []) }
}

// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(todos: todos!.todos)

ContentView.swift

@ObservedObject var todos: RealmSwift.List<Todo>
var body: some View {
    ForEach(todos) { (todo: Todo) in
        Text("\(todo.name)")
        .onDelete(perform: delete)
}

func delete(at offsets: IndexSet) {
    if let realm = todos.realm {
        try! realm.write {
            realm.delete(todos[offsets.first!])
        }
    } else {
        todos.remove(at: offsets.first!)
    }
}

我已经检查了我的待办事项列表的计数和 Realm Studio 中的数据库,它实际上包含索引 5。而且这个崩溃不仅仅针对索引 5,例如如果列表中只有 2 个项目,应用程序会因Index 1 而崩溃,或者当它包含 5 个项目时,应用程序会因Index 4 而崩溃。

该示例具有几乎相同的数据模型,并且可以完美运行。那么我在这里做错了什么?

【问题讨论】:

    标签: ios swift database xcode realm


    【解决方案1】:

    问题在于,通过从您的todos 列表中删除todo,swiftui 无法重建ViewGraph 中的更改,因为该记录显然不再存在。通过首先删除记录,然后将其从您的 todos 列表中删除,由于 @ObservedObject 属性的更改,swiftui 会重新呈现视图。请尝试以下操作:

    1. 尝试将您的领域版本更新到最新版本。目前在5.4.0。他们增加了对 Combine/SwiftUI 的支持。请参阅:https://realm.io/docs/swift/latest/api/Enums/Publishers.html 获取发布者列表。通过使用最新版本,您可以使用以下发布者:

      • objectWillChange - 每次集合或对象更改时发布 void
      • publisher - 每次更改时发布集合
      • changesetPublisher - 每次集合更改时发布 RealmCollectionChangeset,或者如果在领域对象上使用它会发布该对象的更改
      • objectWillChange - 每次对象更改时发布 void
      • freeze() - 冻结集合或对象并使其不可变。
    2. freeze() 你的todos 集合使用ForEach(todos.freeze())。通过冻结集合,SwiftUI 能够进行“渲染”检查。

    另一种方法是订阅列出的发布者并对他们的更改做出相应的反应。

    【讨论】:

    • 我已经在使用最新版本,但我犯了错误。但是在 ForEach 中使用 freeze() 会引发另一个异常“由于未捕获的异常 'RLMException' 而终止应用程序,原因:'对象已由另一个领域管理。使用 create 代替将其复制到此领域。'”。我正在进一步调查。
    • 尝试使用单个领域实例进行操作。
    • 是的,创建新的 Realm 实例肯定是个问题。您能否推荐一种在不同视图之间传递同一 Realm 实例的好方法,以便我可以从不同视图访问不同的模型,而不是在 SceneDelegate 中一次实例化它们?可能是 CoreData 的环境上下文之类的东西?
    • 是的,有很多方法。最简单的答案是使用单例模式。只需创建一个类并添加以下属性:static let shared = Realm()。然后您可以通过SomeClass.shared 使用它
    • 感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多