web-dev-qa-db-ja.com

間違ったスレッドからアクセスされたレルム-再び

レルムオブジェクトへのアクセスに関する多くの問題に気づき、私の解決策がそれを解決すると考えました。

だから私はこのような簡単な支援方法を書いた:

public func write(completion: @escaping (Realm) -> ()) {
    DispatchQueue(label: "realm").async {
        if let realm = try? Realm() {
            try? realm.write {
                completion(realm)
            }
        }
    }
}

完了ブロックは問題ないと思いました。オブジェクトを作成したり更新したりするたびに、上記の方法を使用するからです。

残念ながら、エラーが発生しています:

libc++abi.dylib: terminating with uncaught exception of type realm::IncorrectThreadException: Realm accessed from incorrect thread.
11
Tajnero

RealmおよびObjectのインスタンスはスレッドに含まれています。スレッド間で渡すことはできません。そうしないと、例外が発生します。

キューの作成と同時にcompletionブロック自体をバックグラウンドキューに渡すので(Dave Westonが述べたように)、そのブロック内のRealmオブジェクトは同じ場所に作成されていません。このエラーを説明するスレッド。

Daveが言ったように、そのメソッドを呼び出すたびに新しいディスパッチキューを作成しています。しかし、それを拡張するために、単一のキューが同じスレッドで一貫して呼び出されるという保証もiOSにはありません。

そのため、Realmのベストプラクティスは、同じスレッドで新しい操作を実行するたびに同じスレッドでRealmオブジェクトを再作成することです。レルムは、スレッドごとにRealmのインスタンスを内部的にキャッシュするため、Realm()を複数回呼び出すことに伴うオーバーヘッドはほとんどありません。

特定のオブジェクトを更新するには、 新しいThreadSafeReference機能 を使用して、バックグラウンドスレッドで同じオブジェクトに再アクセスします。

let realm = try! Realm()
let person = Person(name: "Jane") // no primary key required
try! realm.write {
  realm.add(person)
}
let personRef = ThreadSafeReference(to: person)
DispatchQueue(label: "com.example.myApp.bg").async {
  let realm = try! Realm()
  guard let person = realm.resolve(personRef) else {
    return // person was deleted
  }
  try! realm.write {
    person.name = "Jane Doe"
  }
}
23
TiM

メソッドは、呼び出すたびに新しいDispatchQueueを作成します。

DispatchQueue(name:"")は初期化子であり、ルックアップではありません。常に同じキューにいることを確認したい場合は、そのキューへの参照を保存し、キューにディスパッチする必要があります。

レルムのセットアップ時にキューを作成し、セットアップを行うクラスのプロパティとしてキューを保存する必要があります。

7
Dave Weston

一般的に、異なるスレッドで初期化し、異なるスレッドからアクセスまたは変更しようとすると発生します。デバッガを配置して、初期化されたスレッドを確認し、同じスレッドを使用してみてください。

1
MD Aslam Ansari

おそらくそれは誰かを助けます(私は解決策を探して数時間を費やしました)

私の場合、JSONからモデル(ObjectMapper_Realmをインポートした)へのバックグラウンドマッピングでクラッシュが発生しました。同時に、メインスレッドに割り当てられたレルムのインスタンスがありました。

1
Kowboj