由一个线上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
安全移除
- Swift项目优先使用