2017-04-06 6 views
1

をisFinishedするとき、私はそのisFinished財産のKVOの観察のために登録し、操作サブクラスのインスタンスを作成し、私のキューに追加して、操作を起動します。登録解除操作のKVOの観察は、この単純なコード(Xcodeの8.3)で

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が割り当て解除されました」とクラッシュすることです。 「開始」と「終了」を印刷しますが、「観測」は印刷しません。操作が存在しなくなるの前に私のKVO通知を取得します。

これはキャッチ22のようです。 isFinishedを観察してオブザーバーを削除できない場合、どうすればいいですか? [私はmainの末尾に私自身のKVO観察可能なプロパティをMyOperationに追加することでこの問題を回避することができます。しかし、私がこれをしなければならないという考えは非常に奇妙です。これは私がここにしようとしているものを行うことができるようにisFinishedは、観測可能である正確な理由はないのですか?]

+1

https://developer.apple.com/library/content/technotes/tn2109/_index.htmlに記載されている問題がありますか? - "同様の問題は、NSOperationのisFinishedプロパティを観察するためにキー値観測(KVO)を使用するときに発生します。KVOはオブザーバまたはオブザーバを保持しませんが、オブザーバを削除しても-viewWillDisappear:メソッドでは、KVO通知が既にオブジェクトの飛行中である可能性があります。その場合、通知を実行しているスレッドは、割り当て解除されたオブジェクトを呼び出すことになります。 –

+0

@MartinRこのセクションでは、「自己」(オブザーバー)が存在しなくなる可能性があるという話をしているので、KVO通知は存在しないオブジェクトに送信される可能性があります。それは私の問題の_私の問題は、私のKVO通知を送らなくても操作(オブザーバ)が存在しなくなるということです。 'self'はルートビューコントローラであり、どこにも行きません。 – matt

+0

私はそれを試したことに言及する有用性はありますか?「仕上げの観察開始オプション( "isFinished") '?私のアプリはクラッシュしない*。私はxcode 8.2を使用していますが、私はその事件を理解することを願っています... –

答えて

2

のXcode 8.2にまったく同じ与えられたコードスニペットをテストした後、それが必要として、それは、コンソールを働きました示しています

starting 
finishing 
Observed Optional("isFinished") 

問題の理由は、Xcodeの8.3上でそれをテストしているようだ、おそらくそれはバグです - またはそれは新しいbehavior-かもしれません。しかし、バグとして報告することをお勧めします。

+1

さて、このバグはCocoa Foundationのバグではないことが判明しました。これは '#keyPath'を含むSwift言語のバグです。回避策は代わりにリテラル文字列を使用して 'op.addObserver(self、forKeyPath:" isFinished "、options:[]、context:nil)と書くことです。 – matt

+0

スウィフトバグ報告:https://bugs.swift.org/browse/SR-4397私も私のものを提出しましたが、(正しく)重複としてマークされました。 – matt

+0

@mattしかし、私は '#keyPath(MyOperation.isFinished)'の代わりに 'forKeyPath:" isFinished "'として実装する必要があります。 '#selector 'を使って作業するのと同じように見えます。 –

2

AppleがSwift 3.1(source)で#keyPathの動作を変更しました。現在#keyPath(isFinished)"finished"を返し、それは"isFinished"を返しました。はバグでした。ここでは、KVOOperationクラスを使用すると簡単に混乱する可能性があるため説明します。


あなたはKVO通知のためのオブジェクトを登録

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

財団はwillChangeValue(forKey:)didChangeValue(forKey:)this is done via isa-swizzling)を呼び出して、新たなセッターの実装でそれ(textView)を提供します。これらのメソッドに渡されるキーは、getter(isEditable)ではなくsetter(setEditable:)で、プロパティ名(editable)ではありません。

@property(nonatomic,getter=isEditable) BOOL editable 

#keyPath(UITextView.isEditable)からeditableを受けることが予想されるのはこのためです。


Operationクラスが定義されたプロパティがありますが、広告は、それがisFinishedisExecutingキーの通知を遵守することを期待

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

に従います。そのタスクの完了または中止時

、あなたの同時操作対象はisExecutingの両方にKVO通知を生成する必要がありますし、あなたの運転のための状態の最終変更をマークする

をキーパスをisFinishedこれがために私たちを強制的にこれらの通知を送信するときにリテラル文字列を使用します。

IMHOこれは何年も前の間違いであり、既存のコードを壊すことなく元に戻すことは本当に困難です。

+0

はい、私はそれをすべて知っています。 – matt

関連する問題