web-dev-qa-db-ja.com

NSOperationプロパティのオーバーライド(isExecuting / isFinished)

NSOperationをSwiftでサブクラス化しており、isExecutingをオーバーライドしているため、isFinishedプロパティとstartプロパティをオーバーライドする必要があります。方法。

私が遭遇した問題は、これらのプロパティをオーバーライドできる一方で、Key-Value監視(KVO)をどのように保持するかです。

通常、Obj-Cでは、クラス拡張JSONOperation ()定義でプロパティをreadwriteとして再宣言するのはかなり簡単です。ただし、Swiftにはこれと同じ機能はありません。

例:

class JSONOperation : NSOperation, NSURLConnectionDelegate
{
    var executing : Bool
    {
        get { return super.executing }
        set { super.executing } // ERROR: readonly in the superclass
    }

    // Starts the asynchronous NSURLConnection on the main thread
    override func start()
    {
        self.willChangeValueForKey("isExecuting")
        self.executing = true
        self.didChangeValueForKey("isExecuting")

        NSOperationQueue.mainQueue().addOperationWithBlock(
        {
            self.connection = NSURLConnection(request: self.request, delegate: self, startImmediately: true)
        })
    }
}

だからここに私が思いついた解決策があります、しかしそれはひどく醜くてハッキーに感じます:

var state = Operation()

struct Operation
{
    var executing = false
    var finished = false
}

override var executing : Bool
{
    get { return state.executing }
    set { state.executing = newValue }
}

override var finished : Bool
{
    get { return state.finished }
    set { state.finished = newValue }
}

もっと良い方法があると教えてください。 struct全体の代わりにvar isExecutingを作成できることはわかっていますが、あいまいさを導入し、公に書き込み可能にする(私は望まない)2つの同様の名前のプロパティがあります。

ああ、私がいくつかのアクセス修飾子キーワードに対して何をするか...

24
Erik

Swift本から:

サブクラスプロパティオーバーライドでゲッターとセッターの両方を提供することにより、継承された読み取り専用プロパティを読み取り/書き込みプロパティとして提示できます。

これが機能することがわかると思います。

override var executing : Bool {
    get { return _executing }
    set { 
        willChangeValueForKey("isExecuting")
        _executing = newValue 
        didChangeValueForKey("isExecuting")
    }
}
private var _executing : Bool
24
David Berry

Davidが言ったように、サブクラスプロパティオーバーライドでgetterとsetterの両方を実装できます。

ただし、asynchronous/concurrent操作(つまり、非同期で完了する操作)を定義する場合は、willおよびdidChangeValueForKeyに対してisFinished/isExecutingを呼び出すことが重要です。そうしないと、操作が解放されず、依存関係が尊重されず、maxConcurrentOperationCountなどの問題が発生します)。

したがって、私は提案します:

private var _executing: Bool = false
override var executing: Bool {
    get {
        return _executing
    }
    set {
        if _executing != newValue {
            willChangeValueForKey("isExecuting")
            _executing = newValue
            didChangeValueForKey("isExecuting")
        }
    }
}

private var _finished: Bool = false;
override var finished: Bool {
    get {
        return _finished
    }
    set {
        if _finished != newValue {
            willChangeValueForKey("isFinished")
            _finished = newValue
            didChangeValueForKey("isFinished")
        }
    }
}

ちなみに、_executing_finishedが変更されたかどうかを確認することは重要ではありませんが、カスタムのcancelメソッドなどを作成するときに役立つ場合があります。


更新:

何度も、人々はNSOperation.hの新しいfinished/executingプロパティを指摘し、適切なKVOキーはfinished/executingであると結論付けました。一般に、KVO準拠のプロパティを作成する場合、それは正しいでしょう。

しかし、NSOperationQueuefinished/executingキーを監視しません。isFinished/isExecutingキーを監視します。 isFinished/isExecutingキーのKVO呼び出しを実行しないと、問題が発生する可能性があります(特に、非同期操作間の依存関係が失敗します)。それは迷惑ですが、それがどのように機能するかです。 並行実行プログラミングガイド操作キュー の章の並行実行のための操作の構成セクションは非常にisFinished/isExecutingKVO呼び出しを実行する必要があるというトピックを明確にします。

同時実行プログラミングガイドは日付が付けられていますが、isFinished/isExecutingKVOに関しては非常に明確です。また、ガイドが実際のNSOperationの実装を反映していることを簡単に経験的に検証できます。デモンストレーションとして、NSOperationで非同期/並行NSOperationQueueサブクラスを使用する場合の 適切なKVOのこのGithubデモンストレーション の単体テストを参照してください。

29
Rob

Swift 3.0 Answer Update:

private var _executing : Bool = false
override var isExecuting : Bool {
    get { return _executing }
    set {
        guard _executing != newValue else { return }
        willChangeValue(forKey: "isExecuting")
        _executing = newValue
        didChangeValue(forKey: "isExecuting")
    }
}


private var _finished : Bool = false
override var isFinished : Bool {
    get { return _finished }
    set {
        guard _finished != newValue else { return }
        willChangeValue(forKey: "isFinished")
        _finished = newValue
        didChangeValue(forKey: "isFinished")
    }
}
11
Mark