Swift 4 KVO阻止崩溃:在观察者仍然注册时观察到的对象被释放

时间:2018-04-26 21:50:57

标签: ios swift key-value-observing

我最近开始使用iOS 11开发我的应用程序作为目标版本,因为这是默认值。由于原因,我现在将版本降低到9.3。

该应用程序是纯粹的快速4,使用新的KVO块事物。 我用safeAreaInsets和诸如此类的东西修复了我遇到的一些编译时错误,并成功构建了应用程序。一份快速的工作。好的。

我尝试在iPhone 7 iOS 10.3.1模拟器上运行它,并且主 - 这是一个火车残骸。我想UITableViewAutomaticDimension在这些日子里并不是真的回事。

无论如何,我已经解决了大部分布局问题,但现在我遇到了一些硬崩溃问题。无论何时我都使用过这款新的KVO,当我向后导航时它会崩溃。 我的导航推送的ViewController是KVO-监听它所拥有的对象内的一个字段。当我弹出导航时,viewController和对象按顺序被释放,应用程序崩溃,给我这个错误:

*** 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的{​​{1}}中的oberservation代码。

MyObject

我的印象是,这种新的神奇KVO-block风格会解决世界和平问题,但显然只适用于iOS 11。

现在,我已经尝试了一些事情,但我无法让它崩溃。它每次都会发生,我不明白为什么。

由于崩溃日志告诉我观察对象在对象观察时被解除分配,但我也知道观察对象在观察对象之前被释放,我已经尝试在观察者中执行此操作:

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
    }))
}

但这并没有帮助。我也这样做了:

//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

为什么没有这项工作?我必须在何时何地删除&lt;中的观察者? iOS11?

1 个答案:

答案 0 :(得分:7)

看起来你遇到了一年前我报道的一个错误,但不幸的是很少受到关注:

https://bugs.swift.org/browse/SR-5752

由于这个bug在一段时间内没有让我感到困惑,我原本希望它已经在Swift叠加层中修复了,但我只是尝试将我的代码从bug报告复制到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()
}

请确保您只在受此bug影响的iOS版本上执行此操作,否则您将删除已被删除的观察,然后 可能会崩溃。这不好玩吗?