ios - Swift 4 KVO 块崩溃:观察到的对象在观察者仍在注册时被释放
问题描述
我最近开始使用 iOS 11 作为目标版本来开发我的应用程序,因为这是默认值。由于某些原因,我现在已将版本降低到 9.3。
该应用程序是纯 swift 4,使用新的 KVO 块事物。我修复了我遇到的一些编译时错误safeAreaInsets
等等,并且成功构建了应用程序。一个快速的工作。好的。
我尝试在 iPhone 7 iOS 10.3.1 模拟器上运行它,天哪 - 这是火车残骸。我想UITableViewAutomaticDimension
这在当时并不是真的。
无论如何,我已经修复了大部分布局问题,但现在我遇到了一些硬崩溃。我在任何地方都使用过这个新的 KVO,当我返回时它会崩溃。我的导航推送 ViewController 是 KVO 侦听它持有的对象内的字段。当我弹出导航时,视图控制器和对象按顺序被释放,应用程序崩溃,给我这个错误:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x7fdf2e724250 of class MyProject.MyObject was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x60800003fd80> (
<NSKeyValueObservance 0x610000050020: Observer: 0x61000006f140, Key path: isSelected, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x6180000595f0>
)'
据我所知,它表示观察到的对象MyObject
在有人观察变量时被释放isSelected
。
这是观察中MyViewController
的观察代码MyObject
。
var observations:[NSKeyValueObservation] = []
func someFunction() {
observations.removeAll()
let myObject = MyObject(/*init*/)
self.myObject = myObject
observations.append(myObject.observe(\.isSelected, changeHandler: { [weak self] (object, value) in
//Do stuff
}))
}
我的印象是这种新的神奇 KVO 块样式将解决世界和平,但显然这只适用于 iOS 11。
现在,我已经尝试了一些事情,但我无法让它不崩溃。每次都会发生,我不明白为什么。
由于崩溃日志告诉我观察对象在观察对象时被释放,但我也知道观察对象在观察对象之前被释放,我尝试在观察者中这样做:
//In MyViewController
deinit {
observations.forEach({$0.invalidate()})
observations.removeAll()
print("Observers removed")
}
但这无济于事。我也这样做了:
//In MyObject
deinit{
print("MyObject deinit")
}
当我做这件事时 - 我得到以下输出:
>Observers removed
>MyObject deinit
>WORLD WAR 5 STACK TRACE
我也试过
//In MyViewController
deinit{
self.myObject.removeObserver(self, forKeyPath: "isSelected")
}
但我得到的输出是Can't remove because it is not registered as an observer
. 所以我猜想实际上MyViewController
并不是在使用这个新的 KVO 时被附加的观察者。
为什么这些都不起作用?我必须在何时何地删除 < iOS11 中的观察者?
解决方案
看起来您遇到了我大约一年前报告的错误,但不幸的是很少受到关注:
https://bugs.swift.org/browse/SR-5752
由于这个错误已经有一段时间没有咬我了,我希望它已经在 Swift 覆盖中得到修复,但我只是尝试将我的代码从错误报告复制到一个 iOS 项目中并在 10.3.1 模拟器中运行它,果然,崩溃又回来了。
您可以像这样解决它:
deinit {
for eachObservation in observations {
if #available(/*whichever version of iOS fixes this*/) { /* do nothing */ } else {
self.removeObserver(eachObservation, forKeyPath: #keyPath(/*the key path*/))
}
eachObservation.invalidate()
}
observations.removeAll()
}
确保仅在受该错误影响的 iOS 版本上执行此操作,否则您将删除已删除的观察,然后可能会崩溃。这不是很有趣吗?
推荐阅读
- vue.js - 调试涉及多个文件之间的 EventBus 通信的程序
- android - 手机重启(重启)后,在android中首先调用哪个接收器
- android - 颤振中有唤醒锁吗?
- java - Springs ApplicationEvents 的自定义过滤器
- java - 弹簧控制器路径不被视为常数
- git - 如何使用 ajoberstar/reckon 和 Bitbucket Pipelines 配置自动语义版本控制?
- javascript - 我在轮播中显示图像的功能有一个额外的空白图像
- java - 如何在 Struts2 中导出 CSV 文件
- c# - 浮点精度中double和float的区别
- angular - 打字稿将接口描述的类型转换为另一种类型?