iOS 10 - сбой NSKeyValueObservation на deinit - PullRequest
0 голосов
/ 27 апреля 2018

Я использую NSKeyValueObservation для наблюдения свойств в подклассе WKWebView.

Хорошо работает на iOS 11, но вылетает на deinit на iOS 10.


Печатный журнал ошибок на консоли

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x15209e600 of class Rakuemon.WebView was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x170232da0> (
<NSKeyValueObservance 0x170259bf0: Observer: 0x17027d500, Key path: loading, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x170643ba0>
<NSKeyValueObservance 0x170643480: Observer: 0x170c72f80, Key path: estimatedProgress, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x170643330>
<NSKeyValueObservance 0x170642c70: Observer: 0x17086c0c0, Key path: title, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x1706437b0>
)'

Коды

class WebView: WKWebView {

    // MARK: - Properties

    weak var delegate: WebViewDelegate?

    // MARK: - Private properties

    private var contentSizeObserver: NSKeyValueObservation?
    private var loadingObserver: NSKeyValueObservation?
    private var estimatedProgressObserver: NSKeyValueObservation?
    private var titleObserver: NSKeyValueObservation?

    // MARK: - Life cycle

    override init(frame: CGRect, configuration: WKWebViewConfiguration) {
        super.init(frame: frame, configuration: configuration)
        setupObserver()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

// MARK: - Private functions

private extension WebView {
    func setupObserver() {
        contentSizeObserver = scrollView.observe(\.contentSize, options: [.old, .new], changeHandler: { [unowned self] _, change in
            guard let oldSize = change.oldValue, let newSize = change.newValue, oldSize != newSize else { return }
            self.delegate?.webView?(self, didChangeSizeFrom: oldSize, to: newSize)
        })

        loadingObserver = observe(\.isLoading, changeHandler: { [unowned self] _, _ in
            self.delegate?.webViewIsLoading?(self)
        })

        estimatedProgressObserver = observe(\.estimatedProgress, options: [.new], changeHandler: { [unowned self] _, change in
            guard let newValue = change.newValue else { return }
            self.delegate?.webView?(self, didChangeEstimatedProgress: newValue)
        })

        titleObserver = observe(\.title, options: [.new], changeHandler: { [unowned self] _, change in
            guard let title = change.newValue else { return }
            self.delegate?.webView?(self, didChangeTitle: title ?? "")
        })
    }
}

Вопрос

Я также обнаружил, что contentSizeObserver, который наблюдал scrollView.contentSize, а не свойства self, не вызывал сбой.

Итак, как правильно наблюдать свойства self на iOS 10 - NSKeyValueObservation? или отменить регистрацию?

Ответы [ 3 ]

0 голосов
/ 19 июня 2018

Я в конце концов выясняю, как удалить наблюдателя в deinit на iOS 10.

deinit {
    if let titleObserver = titleObserver {
        titleObserver.invalidate()
        removeObserver(titleObserver, forKeyPath: "title")
        self.titleObserver = nil
    }
}
0 голосов
/ 09 ноября 2018

Это сработало для меня:

deinit {
    titleObserver = nil
}
0 голосов
/ 21 мая 2018

Похоже, яблочный жук - https://bugs.swift.org/browse/SR-5816

Это происходит потому, что NSKeyValueObservation содержит слабую ссылку на объект. Эта слабая ссылка слишком быстро превращается в ноль.

В некоторых случаях обходной путь можно найти с помощью методов жизненного цикла (viewDidDissapear и т. Д.). В других случаях вы должны использовать старый API obj-c (addObserver / removeObserver / наблюдай значение) для поддержки iOS 10.

...