iOS 10下NSKeyValueObservation在deinit阶段崩溃问题
解决iOS 10下WKWebView子类使用NSKeyValueObservation deinit崩溃问题
这个坑我之前踩过!核心原因是iOS 10中NSKeyValueObservation的自动失效机制存在局限性,尤其是WKWebView这类有特殊生命周期的子类,系统没法自动在对象释放前移除绑定的观察者,最终触发NSInternalInconsistencyException崩溃。
解决方案:手动接管观察者生命周期
iOS 11及以后,NSKeyValueObservation会自动在被观察对象销毁时失效,但iOS 10必须我们手动干预。具体操作如下:
把观察者实例存为类属性
别在初始化方法里直接创建临时观察者,要把每个NSKeyValueObservation对象存为子类的私有属性,确保在deinit阶段能访问到它。在deinit中显式调用
invalidate()
强制触发观察者失效,彻底解除它和当前对象的绑定,避免对象释放时还有残留的观察者注册信息。
完整代码示例
class WebView: WKWebView { // 每个监听对应一个属性,保存观察者实例 private var contentOffsetObservation: NSKeyValueObservation? // 多个监听就添加多个属性 // private var titleObservation: NSKeyValueObservation? override init(frame: CGRect, configuration: WKWebViewConfiguration) { super.init(frame: frame, configuration: configuration) setupObservers() } required init?(coder: NSCoder) { super.init(coder: coder) setupObservers() } private func setupObservers() { // 创建观察者并绑定到属性 contentOffsetObservation = observe(\.scrollView.contentOffset, options: [.new]) { [weak self] webView, change in // 监听逻辑里用weak self避免潜在循环引用 guard let self = self else { return } print("Content offset changed: \(change.newValue ?? .zero)") } // 其他监听示例 // titleObservation = observe(\.title, options: [.new]) { _, change in // print("WebView title updated: \(change.newValue ?? "")") // } } deinit { // 手动失效所有观察者 contentOffsetObservation?.invalidate() // titleObservation?.invalidate() } }
额外注意事项
- 闭包里一定要加
[weak self],虽说NSKeyValueObservation本身不会造成循环引用,但能规避其他潜在的内存泄漏风险。 - 多监听场景下,每个观察者都要单独存为属性并在
deinit中失效,不能只处理其中一个。
这个方案既能解决iOS 10的崩溃问题,在iOS 11+上也能正常运行,完全兼容所有目标版本。
内容的提问来源于stack exchange,提问作者mrfour
相关产品推荐
相关产品推荐

