Swift 何时取消注册KVO操作的观察已完成
在这个简单的代码(Xcode 8.3)中,我创建了一个操作子类实例,注册其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
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)看看我能不能确认。我会告诉你我发现了什么。谢谢!