Swift Combine:删除核心数据实体后调用 sink()

Swift Combine: sink() called after core data entity deletion

我在 SwiftUI 视图模型中使用此代码:

    car.publisher(for: \.sold, options: [.new])
        .removeDuplicates()
        .receive(on: RunLoop.main)
        .sink { [weak self] sold in
            guard let self = self else { return }
            .... here is reference the car entity for some logic ....
        }
        .store(in: &subscribers)

一切正常,直到我真正删除了那个 Car 实体并且接收器开始运行然后 .... here is reference the car entity for some logic .... 运行并且它在尝试使用 DELETED Core Data 实体时崩溃。

我的模式在这里似乎是错误的。当那个汽车实体从上下文中删除时,有没有办法让那个接收器自动取消?

您可以使用 faultingState 来跟踪此场景。来自 documentation:

0 if the object is fully initialized as a managed object and not transitioning to or from another state, otherwise some other value. This property allows you to determine if an object is in a transitional phase when receiving a key-value observing change notification.

所以你可以像这样忽略这个事件:

.sink { [weak self] sold in
    guard let self = self, car.faultingState == 0 else { return }
    //
}

如果你真的想取消这个接收器,你可以在对象中存储可取消的,这样你就可以在 prepareForDeletion 期间取消它们。

为此,您需要更改目标代码生成,可以找到更多信息 here。更改为 Category/Extension - 在这种情况下,您可以创建一个 class 并覆盖 prepareForDeletion,并且 Xcode 仍会生成所有属性给你。

现在您可以将所有发布者逻辑移至您的 class:

@objc(Car)
public class Car: NSManagedObject {
    private var subscribers = Set<AnyCancellable>()

    func observe<Value: Equatable>(keyPath: KeyPath<Item, Value>, receiveValue: @escaping ((Value) -> Void)) {
        publisher(for: keyPath, options: [.new])
            .removeDuplicates()
            .receive(on: RunLoop.main)
            .sink(receiveValue: receiveValue)
            .store(in: &subscribers)
    }

    public override func prepareForDeletion() {
        super.prepareForDeletion()
        subscribers.removeAll()
    }
}

或者只是用它来存储可取消项。