何时取消注册KVO观察Operation isFinished

时间:2017-04-06 19:38:23

标签: swift key-value-observing nsoperation

在这个简单的代码(Xcode 8.3)中,我创建一个Operation子类实例,注册其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被解除分配,而键值观察者仍然注册了#34;。我们打印"开始"和"整理"但我们从不打印"观察&#34 ;;在我获得KVO通知之前,操作不存在

这看起来像是一个捕获22。如果我不能通过观察isFinished来移除观察者,我应该什么时候这样做? [我可以通过向MyOperation添加我在main末尾设置的自己的KVO可观察属性来解决此问题。但我必须这样做的想法很奇怪;这不是isFinished可观察的原因,所以我可以做我想在这里做的事情吗?]

2 个答案:

答案 0 :(得分:2)

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

starting
finishing
Observed Optional("isFinished")

问题的原因似乎是在 Xcode 8.3 上测试它,可能是一个错误 - 或者它可能是一个新行为 - 。但是,我建议将其报告为错误。

答案 1 :(得分:2)

Apple改变了Swift 3.1中的#keyPath行为(source)。目前#keyPath(isFinished)会返回"finished",它会返回"isFinished"会返回错误。以下是解释,因为使用KVOOperation类时很容易引起混淆。

为KVO通知注册对象时

textView.addObserver(self,
                     forKeyPath: #keyPath(UITextView.isEditable),
                     options: [.new, .old],
                     context: nil)

Foundation为其(textView)提供了调用willChangeValue(forKey:)didChangeValue(forKey:)this is done via isa-swizzling)的新setter实现。传递给这些方法的密钥不是getter(isEditable)而不是setter(setEditable:),而是属性名称(editable)。

@property(nonatomic,getter=isEditable) BOOL editable

这就是为什么预计会从editable收到#keyPath(UITextView.isEditable)

虽然Operation类具有定义的属性,但是

@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;

它希望观察isFinishedisExecuting键的通知。

  

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

这会强制我们在发布这些通知时使用文字字符串。

恕我直言,这是多年前的错误,如果不破坏现有代码,很难恢复。