Почему я получаю аварийную очистку блочного KVO на себя с NSManagedObject? - PullRequest
3 голосов
/ 04 ноября 2019

У меня есть модель базовых данных со сложными отношениями, которые я смоделировал с промежуточными объектами «Соединение» (дальнейший контекст в этом вопросе: Ведение сложных однонаправленных отношений в базовых данных ).

Я не хочу, чтобы код, использующий модель, знал о промежуточных объектах соединения, поэтому я добавляю свойство массива к моим основным управляемым объектам, которое предоставляет доступ к объектам, к которым они подключены (которые я тоже хочу наблюдать).

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

Приведенный выше код устанавливает наблюдение за NSOrderedSet соединений при инициализации массива hosts (используя универсальную функцию, которая может создать этот шаблон для всех моих типов соединений). Наблюдения запускаются, как и ожидалось.

Я лишил законной силы наблюдение в 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

Как вы можете видеть из журнала, я лишил законной силы наблюдение непосредственно перед аварией, утверждая, что это проблема. Если я попытаюсь установить для наблюдения значение nil в willTurnToFault(), мое приложение в этот момент вылетает.

Я новичок в 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?

    }

Я не понимаю, что происходит с hostsObservation, чтобы установить его на nil в willTurnToFault() сбой.

Я строю для OSX 10.12, используя Swift 5 в Xcode 11.1.

РЕДАКТИРОВАТЬ 3:

Воспроизведено в новом простом проекте: https://github.com/GilesHammond/KVO-Core-Data-Crash

...