viewWillAppear与viewWillDisappear不匹配问题

由一个线上crash引起, 崩溃信息Cannot remove an observer because it is not registered as an observer.

崩溃代码如下所示:

swift 复制代码
class ObserverViewController: UIViewController {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("viewWillAppear - ADD Observer")
        FakeNetworkManager.shared.addObserver(self, forKeyPath: "networkStatus", options: [.new, .old], context: nil)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("viewWillDisappear - REMOVE Observer")
        FakeNetworkManager.shared.removeObserver(self, forKeyPath: "networkStatus")
    }

    override func observeValue(forKeyPath keyPath: String?, of _: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("Observed network status changed.")
    }
}
  • 崩溃原因 :账号被挤下线时系统执行viewWillDisappear进行removeObserver,但此时尚未执行addObserver
  • 当用户在某个业务场景下,被其他设备登录相同账号挤掉时
  • 此时ObserverViewController已经在页面层级中,而且已经经过了viewWillAppear和viewWillDisappear过程,并且不在页面层级最上层了
  • 当前设备中需要关掉所有页面回到登录页面,此时系统主动触发了viewWillDisappear,进而导致crash

解决办法

1. try-catch方案

如果是Objective C语言的话,可以通过trycatch捕获异常

less 复制代码
- (void)safeRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
    @try {
        [self removeObserver:observer forKeyPath:keyPath];
    } @catch (NSException *exception) {
        DDLogError(@"exception:%@", exception);
    }
}

2. NSKeyValueObservation(Swift方案)

swift 复制代码
private func addNetworkStatusObserver() {
    networkStatusObservation = MCNetworkStatusManager.sharedInstance().observe(.networkStatus, options: [.new, .old]) { [weak self] manager, _ in
        guard let self else { return }
        let currentStatus = manager.networkStatus.rawValue
         if currentStatus > MCNetworkStatus.notReachable.rawValue {
            self.reloadSafeWithFetchTop50PrivateChatUserFullinfo(true)
        }
    }
}

private func removeNetworkStatusObserver() {
    networkStatusObservation?.invalidate()
    networkStatusObservation = nil
}

注意 :NSKeyValueObservation存在监听RawRepresentable类型数据时change结果异常的未修复bug

3. 第三方KVO组件

比如facebook的KVOController

总结

  • 核心结论 :线上复杂场景中,viewWillAppear和viewWillDisappear不一定配对出现

  • 推荐方案:

    • Swift项目优先使用NSKeyValueObservation
    • Objective-C项目使用try-catch安全移除
相关推荐
2501_9151063236 分钟前
KXApp 的 VS Code 真机调试:按下 Cmd+R 之后发生了什么
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
ii_best2 小时前
安卓手机开发、iOS 手机开发都能用:按键精灵远程调试,设备在千里外也能改脚本
android·ios·智能手机·自动化·按键精灵
2601_955518182 小时前
36. 跨语言:C/C++与Swift&Objective-C混合编程(一)
objective-c·swift·c/c++·混合编程·跨语言
开心猴爷16 小时前
应对iOS审核4.3:可以使用 IpaGuard 来给属性名混淆的技术细节与平衡之道
后端·ios
iOS开发上架哦16 小时前
iOS应用耗电测试:使用Energy Log、Keymob和Instruments进行性能监控与能源消耗分析
后端·ios
_瑞17 小时前
APM_KSCrash 原理
ios
茶底世界之下1 天前
窗光静物一定要更亮吗?用 Awaking 把曝光收回一点
ios
ii_best1 天前
ios手机开发安卓脚本开发软件[按键精灵教程]从「找最小值」到「Boss 最近刷新」再到「屏幕范围极值」
android·ios·智能手机·自动化·按键精灵
东坡肘子1 天前
谁是 AI-relay App 的真正威胁 -- 肘子的 Swift 周报 #158
人工智能·swiftui·swift
叫Guava不是瓜娃2 天前
我用 Swift 写了个本地语音输入:按住说话,松开就是整理好的文字(开源)
swift