为什么我使用NSManagedObject在self上获得基于崩溃清除块的KVO



我有一个核心数据模型,它具有复杂的关系,我用中间的"连接"对象对其进行了建模(本问题的进一步内容:在核心数据中维护复杂的单向关系(。

我不希望使用模型的代码知道中间连接对象,所以我在我的主托管对象中添加了一个数组属性,它公开了连接到的对象(我也希望它是可观察的(。

@objc dynamic public internal(set) lazy var hosts: [Point] = {
let initialHosts = hostConnections.map { $0.superpoint }
hostsObservation = track(Matter.hostConnections_!, on: self,
mapping: HostConnection.superpoint, to: #keyPath(hosts))
return initialHosts
}()

上面的代码在初始化hosts数组时(使用一个可以在我的所有连接类型上创建此模式的通用函数(对连接的NSOrderedSet进行了观察。观测结果不出所料。

我使willTurnIntoFault():中的观察无效

override public func willTurnIntoFault() {
hostsObservation?.invalidate()
print("hostsObservation (hostsObservation) invalidated")
super.willTurnIntoFault()
}

但是,当这个对象在NSKVODeallocate中被解除分配时,我会崩溃。

hostsObservation Optional(<_NSKeyValueObservation: 0x600000ccd920>) invalidated
2019-11-04 12:02:06.467620+0000 Frame[27900:5614740] [General] An instance 0x60000300bc30 of class FrameGraph.Subject_Subject_ was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x6000002d6ae0> (
<NSKeyValueObservance 0x600000ccd950: Observer: 0x600000ccd920, Key path: hostConnections_, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x600000c78b10>
...
0   CoreFoundation                      0x00007fff3f590e45 __exceptionPreprocess + 256
1   libobjc.A.dylib                     0x00007fff6a1e63c6 objc_exception_throw + 48
2   CoreFoundation                      0x00007fff3f590c77 +[NSException raise:format:] + 193
3   Foundation                          0x00007fff4180f349 NSKVODeallocate + 442
4   CoreData                            0x00007fff3f060772 -[_PFManagedObjectReferenceQueue _processReferenceQueue:] + 1154

正如你从日志中看到的,我在坠机前宣布观察结果无效,声称这是一个问题。如果我尝试将观察设置为nilwillTurnToFault()中,我的应用程序此时崩溃。

我是核心数据的新手,有人能帮我解决我在这里缺少的东西吗?为什么一个观察自己属性的对象会以这种方式崩溃?

编辑1:

我的跟踪功能如下:

internal func track<P: Point, C: Connection>(_ trackedPath: KeyPath<P, NSOrderedSet>,
on point: P,
mapping connectionPath: KeyPath<C, Point>,
to mappedPoints: String)
-> NSKeyValueObservation
{
return point.observe(trackedPath) { [unowned point] data, change in
// ...
}
}

编辑2:

我已经把崩溃的代码剥离回这个:

public class Matter: Point
{
override public func awakeFromFetch() {
super.awakeFromFetch()
print("(hosts)") // Initialise lazy member and set observation
}
public override func willTurnIntoFault() {
super.willTurnIntoFault()
hostsObservation = nil // <-- EXC_BAD_ACCESS 
}
@objc dynamic public internal(set) lazy var hosts: [Point] = {
hostsObservation = observe(.hostConnections_) { [unowned self] data, change in } // Empty observation closure
return [] 
}()
private var hostsObservation: NSKeyValueObservation?
}
// Matter+CoreDataProperties.swift - auto generated
extension Matter {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Matter> {
return NSFetchRequest<Matter>(entityName: "Matter")
}
@NSManaged public var hostConnections_: NSOrderedSet?
}

我不明白hostsObservationwillTurnToFault()崩溃时设置为nil会发生什么。

我正在为OSX 10.12构建,使用Xcode 11.1中的Swift 5。

编辑3:

在一个新的简单项目中复制:https://github.com/GilesHammond/KVO-Core-Data-Crash

关键问题似乎是我的核心数据模型在一个模块中。

我已经更新了示例项目,在这里显示了潜在的bug。

我已经与苹果的支持人员进行了交谈。似乎有一个系统错误。当NSManagedObject位于模块中时,Swift当前无法正确清除该对象上的NSKeyValueObservation。

我已经通过反馈助手向苹果提交了一个问题。

最新更新