Swift 何时取消注册KVO操作的观察已完成

Swift 何时取消注册KVO操作的观察已完成,swift,key-value-observing,nsoperation,Swift,Key Value Observing,Nsoperation,在这个简单的代码(Xcode 8.3)中,我创建了一个操作子类实例,注册其isFinished属性的KVO观察,并通过将其添加到我的队列来启动操作: class MyOperation : Operation { override func main() { print("starting") print("finishing") } } class ViewController: UIViewController { let q = O

在这个简单的代码(Xcode 8.3)中,我创建了一个操作子类实例,注册其
isFinished
属性的KVO观察,并通过将其添加到我的队列来启动操作:

class MyOperation : Operation {
    override func main() {
        print("starting")
        print("finishing")
    }
}

class ViewController: UIViewController {
    let q = OperationQueue()
    override func viewDidLoad() {
        super.viewDidLoad()
        let op = MyOperation()
        op.addObserver(self, forKeyPath: #keyPath(MyOperation.isFinished), options: [], context: nil)
        self.q.addOperation(op)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("Observed \(keyPath)")
        if let op = object as? Operation {
            op.removeObserver(self, forKeyPath: #keyPath(MyOperation.isFinished))
        }
    }
}
如您所见,我有一个
observeValue(forKeyPath…
)的实现,当然,我的计划是在那里调用
removeObserver(forKeyPath…

问题是,我的应用程序因“MyOperation已解除分配,而key value Observators仍在注册”而崩溃。我们打印“starting”和“finishing”,但从未打印“Observed”;在我收到KVO通知之前,该操作已不复存在


这似乎是第二十二条军规。如果我不能通过观察
isFinished
来移除观察者,那么我应该什么时候做?[我可以通过在MyOperation中添加我自己在
main
末尾设置的KVO observable属性来解决这个问题。但是我必须这样做的想法非常奇怪;这不正是
isFinished
是可观察的,这样我就可以做我在这里试图做的事情的原因吗?]

在Xcode 8.2上测试了完全相同的给定代码段后,它正常工作,控制台显示:

starting
finishing
Observed Optional("isFinished")

问题的原因似乎是在Xcode 8.3上对其进行测试,可能是一个bug,也可能是一个新的行为。但是,我建议将其报告为bug。

在Xcode 8.2上测试完完全相同的给定代码片段后,它正常工作,控制台显示:

starting
finishing
Observed Optional("isFinished")
问题的原因似乎是在Xcode 8.3上对其进行测试,可能是一个bug——或者可能是一个新的行为。但是,我建议将其报告为bug。

苹果在Swift 3.1()中更改了
\keyPath
行为。目前
\keyPath(isFinished)
返回
“finished”
,它用于返回
“isFinished”
,这是一个bug。下面是解释,因为在使用
KVO
操作
类时很容易混淆


为KVO通知注册对象时

textView.addObserver(self,
                     forKeyPath: #keyPath(UITextView.isEditable),
                     options: [.new, .old],
                     context: nil)
基金会为它(
textView
)提供了新的setter实现,它调用
willChangeValue(forKey:)
didChangeValue(forKey:)
()。传递给这些方法的键不是getter(
isEditable
)不是setter(
setEditable:
),而是属性名(
editable

这就是它希望从
#keyPath(UITextView.isEditable)
接收
可编辑的原因


尽管Operation类有如下定义的属性

@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
它希望观察
isFinished
isExecuting
键的通知

完成或取消其任务后,并发操作对象必须为isExecuting和isFinished密钥路径生成KVO通知,以标记操作的最终状态更改

这迫使我们在发布这些通知时使用文本字符串

我想这是多年前犯下的一个错误,如果不破坏现有的代码,很难从中恢复。

苹果改变了Swift 3.1()中的
#keyPath
行为。目前
#keyPath(isFinished)
返回
“finished”
,它用来返回
“isFinished”“
这是一个bug。下面是解释,因为使用
KVO
操作
类时很容易混淆


为KVO通知注册对象时

textView.addObserver(self,
                     forKeyPath: #keyPath(UITextView.isEditable),
                     options: [.new, .old],
                     context: nil)
基金会为它(
textView
)提供了新的setter实现,它调用
willChangeValue(forKey:)
didChangeValue(forKey:)
()。传递给这些方法的键不是getter(
isEditable
)不是setter(
setEditable:
),而是属性名(
editable

这就是它希望从
#keyPath(UITextView.isEditable)
接收
可编辑的原因


尽管Operation类有如下定义的属性

@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
它希望观察
isFinished
isExecuting
键的通知

完成或取消其任务后,并发操作对象必须为isExecuting和isFinished密钥路径生成KVO通知,以标记操作的最终状态更改

这迫使我们在发布这些通知时使用文本字符串


IMHO这是多年前犯下的一个错误,如果不破坏现有代码,很难从中恢复。

这可能是?–“使用键值观察(KVO)时可能会出现类似问题”中提到的问题吗观察NSOperation的isFinished属性。虽然KVO既不保留观察者也不保留被观察者,但即使在-viewWillEnglish:方法中删除观察者,也有可能对象已经发出KVO通知。如果发生这种情况,运行通知的线程可能会最终调用“一个被解除分配的对象!”@MartinR注释的这一部分讨论的是
self
(观察者)可能不存在的危险,因此KVO通知可能被发送到一个不存在的对象。这与我的问题相反;我的问题是操作(观察者)在没有向我发送KVO通知的情况下,将不存在。
self
是根视图控制器,并且没有任何进展。提到我尝试过它,并且我能够看到:
开始完成观察到的可选(“isFinished”)是否有用
?我的应用程序没有崩溃。我使用的是xcode 8.2,我希望我了解情况…@AhmadF,如果你看到结果并且没有崩溃,在xcode 8.2中,这表明这可能是(难以置信的)xcode 8.3中的一个新错误。我会在我的另一台机器上尝试(仍然有xcode 8.2)看看我能不能确认。我会告诉你我发现了什么。谢谢!