web-dev-qa-db-fra.com

NSKeyValueObservation: impossible de supprimer un observateur du chemin d'accès à la clé de l'objet car celui-ci n'est pas enregistré en tant qu'observateur

Je rencontre des plantages aléatoires (que je ne peux pas reproduire sur des appareils que je possède) dans mon application, à l'exception de:

Impossible de supprimer un observateur Foundation.NSKeyValueObservation 0xaddress pour le chemin d'accès à la clé "readyForDisplay" de AVPlayerLayer 0xaddress car il n'est pas enregistré en tant qu'observateur.

Cela se produit lorsque je désalloue une UIView contenant AVPlayerLayer.

Mon init:

private var playerLayer : AVPlayerLayer { return self.layer as! AVPlayerLayer }

init(withURL url : URL) {
    ...
    self.asset = AVURLAsset(url: url)
    self.playerItem = AVPlayerItem(asset: self.asset)
    self.avPlayer = AVPlayer(playerItem: self.playerItem)
    super.init(frame: .zero)
    ...
    let avPlayerLayerIsReadyForDisplayObs = self.playerLayer.observe(\AVPlayerLayer.isReadyForDisplay, options: [.new]) { [weak self] (plLayer, change) in ... }
    self.kvoPlayerObservers = [..., avPlayerLayerIsReadyForDisplayObs, ...]
    ...
    }

Mon deinit où exception est levée:

deinit {
    self.kvoPlayerObservers.forEach { $0.invalidate() }
    ...
    NotificationCenter.default.removeObserver(self)
}

Selon Crashlytics, cela se produit sur iOS 11.4.1 sur différents iPhones.

Le code menant à deinit est assez simple:

// Some UIViewController context.
self.viewWithAVLayer?.removeFromSuperview()
self.viewWithAVLayer = nil

J'apprécierais toutes les pensées sur pourquoi cela se produit.

J'ai vu ce bogue mais cela ne semble pas être la cause pour moi.

EDIT 1:

Informations supplémentaires pour la postérité. Sur iOS 10, si je n'invalide pas, j'obtiens un crash reproductible sur deinit. Sur iOS 11, cela fonctionne sans invalidation (pas encore vérifié si le crash disparaît si je n'invalide pas et laisse les observateurs être deinited avec ma classe).

EDIT 2:

Informations supplémentaires pour la postérité: J'ai aussi trouvé ce bug Swift qui pourrait être lié - SR-6795 .

13
iur

Après

self.kvoPlayerObservers.forEach { $0.invalidate() }

Ajouter

self.kvoPlayerObservers.removeAll()

Aussi, je n’aime pas cette ligne:

self.kvoPlayerObservers = [..., avPlayerLayerIsReadyForDisplayObs, ...]

kvoPlayerObservers devrait être un ensemble et vous devriez insérer des observateurs un par un à mesure que vous les recevez. 

7
matt

J'ai accepté la réponse de matt mais je souhaite fournir davantage d'informations sur la manière dont j'ai résolu mon problème.

Mon déinit qui ne tombe pas en panne ressemble à ceci:

if let exception = tryBlock({ // tryBlock is Obj-C exception catcher.
        self.kvoPlayerObservers.forEach { $0.invalidate() };
        self.kvoPlayerObservers.removeAll()
}) {
    remoteLoggingSolution.write(exception.description)
}
... // do other unrelated stuff

Fondamentalement, j'essaie d'attraper l'exception Obj-C si elle se produit et d'essayer de la journaliser à distance.

Ce code est en production depuis deux semaines et depuis lors, je n’ai reçu ni plantage, ni consignation des exceptions. Je suppose donc que la proposition de matt d’ajouter kvoPlayerObservers.removeAll() était correcte (du moins dans mon cas particulier).

0
iur