web-dev-qa-db-fra.com

Royaume accédé par un thread incorrect - encore une fois

J'ai remarqué beaucoup de problèmes avec l'accès aux objets du royaume, et je pensais que ma solution résoudrait ce problème.

J'ai donc écrit une méthode d'aide simple comme celle-ci:

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

Je pensais que le bloc d'achèvement irait bien, parce que chaque fois que j'écris un objet ou le met à jour, j'utilise cette méthode ci-dessus. 

Malheureusement, je reçois une erreur:

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

Les instances de Realm et Object sont contenues dans un thread. Ils ne peuvent pas être passés entre les threads ou cette exception se produira.

Puisque vous passez le bloc completion à la file d’arrière-plan en même temps que la création de la file (comme l’a dit Dave Weston), aucun objet Realm de ce bloc n’aura certainement été créé sur le même thread, ce qui expliquerait cette erreur.

Comme Dave l'a dit, vous créez une nouvelle file d'attente chaque fois que vous appelez cette méthode. Cependant, pour en savoir plus, iOS ne garantit pas non plus qu'une seule file d'attente sera appelée de manière cohérente sur le même thread.

En tant que tel, la meilleure pratique avec Realm consiste à recréer vos objets Realm sur le même thread chaque fois que vous souhaitez effectuer une nouvelle opération sur ce thread. Le domaine met en cache en interne les instances de Realm sur une base par thread, de sorte que la surcharge liée à l'appel de Realm() à plusieurs reprises est minime.

Pour mettre à jour un objet spécifique, vous pouvez utiliser la nouvelle fonctionnalité ThreadSafeReference pour accéder de nouveau au même objet sur un fil d’arrière-plan.

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"
  }
}
19
TiM

Votre méthode crée une nouvelle variable DispatchQueue chaque fois que vous l'appelez. 

DispatchQueue(name:"") est un initialiseur, pas une recherche. Si vous voulez vous assurer que vous êtes toujours dans la même file d'attente, vous devez stocker une référence à cette file d'attente et la distribuer. 

Vous devez créer la file d'attente lors de la configuration du domaine et la stocker en tant que propriété de la classe qui effectue la configuration. 

5
Dave Weston

Peut-être que ça aide quelqu'un (j'ai passé quelques heures à chercher une solution)

Dans mon cas, le mappage d'arrière-plan de JSON sur un modèle (qui a importé ObjectMapper_Realm) est tombé en panne. Au même moment, une instance de royaume était allouée sur le thread principal.

0
Kowboj