我有一个复杂关系的Core Data模型,我用中间的“连接”对象对其进行建模(更多上下文信息请参见此问题:在Core Data中维护复杂的单向关系)。
我不希望使用该模型的代码知道中间连接对象,因此我正在将一个数组属性添加到我的主要托管对象中,以公开连接的对象(我也希望这些对象是可观察的)。
@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
如您从日志中看到的那样,在崩溃之前,我正在使观察失效,并声称它是一个问题。如果我尝试在
willTurnToFault()
中将观察设置为nil
,我的应用程序会在那一点上崩溃。我是Core Data的新手,有谁能帮我理解我在这里缺少了什么?为什么一个观察自己属性的对象会以这种方式崩溃?
编辑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?
}
我不明白为什么在 willTurnToFault()
中将 hostsObservation
设置为 nil
会导致崩溃。
我正在使用 Swift 5 和 Xcode 11.1 构建适用于 OSX 10.12 的应用程序。
第三次编辑:
在一个新的简单项目中重现:https://github.com/GilesHammond/KVO-Core-Data-Crash
Observer: 0x600000ccd920
的类是什么?NSTreecontroller
是否在观察它? - Willeke