16
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Operation(NSOperation)について

Last updated at Posted at 2017-12-31

もっと簡潔にこちらでまとめ直しています。
https://qiita.com/ysn551/items/2a5afc78006d2b22cce0

概要

Operation(NSOperation)についてまとめたものです。
Operationは、concurrentタイプとnon-concurrentタイプに別れますが、non-concrentオペレーションは並列で実行されないう意味ではありません。どちらのタイプのオペレーションも並列で処理されます。
non-concurrentタイプは、同期的な処理(画像の生成などの重たい処理)をオペレーション化したいときに実装します。concurrentタイプは、非同期的な処理(ネットワークからのデータの取得など)をオペレーション化したい時に実装します。
なので並列(concurrent)と非同期(asynchronous)の意味がこんがらがり、この命名は最悪だと思います。

検証環境

端末: iPhone8 plus simulator
iOS: 11
swif: 4.0.3 

まとめ

  • Operationには以下の2種類存在し、実装するタスクが同期的か非同期的かで、どちらを実装するかを決める

    • non-concurrentオペレーション = 同期タスクを非同期かつオペレーション化する
    • concurrentオペレーション = 非同期タスクをオペレーション化する
  • non-concurrentオペレーション

    • 同期的なタスクを非同期化したい場合はこちらを実装する
    • mainメソッドのみオーバーライドする
    • mainメソッド終了後、OperationQueueから削除される
      従ってmainメソッド内で非同期的な実装が出来ないため、非同期的なタスクをオペレーション化したい場合は、concurrentタイプを実装する
  • concurrentオペレーション

    • 非同期的なタスクをオペレーション化したい場合はこちらを実装する

    • startメソッドをオーバーライドする

    • startメソッドが終了してもqueueから削除されない

    • queueからの削除は isFinished プロパティの値が true になったとき (KVOで通知する)

    • 以下の状態をプログラマが管理し、値の変更はKVO通知する必要がある

      • isExecuting = 非同期タスク実行前にtrueにする
      • isFinished = タスク完了時にtrueにする
    • isExecuting, isFinishedプロパティの実装例
      setterを実装することでKVOを便利に行える

      isFinishedの実装例.swift
      var _finished: Bool = false
      override var isFinished: Bool {
          get {
              return _finished
          }
          set {
              //値が変更された場合はKVO通知する
              self.willChangeValue(for: \SubOperation.isFinished)
              _finished = newValue
              self.didChangeValue(for: \SubOperation.isFinished)
          }
      }
      

サンプルコード

non-concurrentタイプ

  • 以下のメソッドをオーバーライドする必要がある
    • main()
non_concurrent_operation_sample.swift
class RequestOperation: Operation {
    func main() {
        //同期的なタスクを実装すること
        self.createImage()
        //main関数終了後自動的にqueueから削除される
    }
}

concurrentタイプ

  • 以下のメソッドをオーバーライドする必要がある

    • start()
    • isExecuting { get }
    • isFinished { get }
    • isAsynchronous { get } // デフォルトでtrue、しかし現状iOSでも無視されている
  • 以下の値の変更は、KVO通知する必要がある

    • isExecuting = 非同期処理開始前にtrueにする
    • isFinished = 非同期処理完了後にtrueにする
  • queueからの削除は isFinished がtrueになったときである

concurrent_operation_sample.swift
class RequestOperation: Operation {

    var object: Object? = nil
    
    enum State {
        case ready
        case executing
        case finished
    }

    private var state = State.ready {
        didSet {
            switch self.state {
            case .ready: break
            case .executing:
                self.isExecuting = true
            case .finished:
                self.isExecuting = false
                self.isFinished = true
            }
        }
    }

    private var _executing: Bool = false
    override var isExecuting: Bool {
        get {
            return _executing
        }
        set {
            if _executing != newValue {
                self.willChangeValue(for: \RequestOperation.isExecuting)
                _executing = newValue
                self.didChangeValue(for: \RequestOperation.isExecuting)
            }
        }
    }

    private var _finished: Bool = false;
    override var isFinished: Bool {
        get {
            return _finished
        }
        set {
            if _finished != newValue {
                self.willChangeValue(for: \RequestOperation.isFinished)
                _finished = newValue
                self.didChangeValue(for: \RequestOperation.isFinished)
            }
        }
    }

    init(object: Object?) {
        super.init()
        self.object = object
    }

    override func start() {
        guard self.isCancelled == false else {
            // Queueから削除される
            self.state = .finshed
            return
        }

        self.state = .executing

        // データストアへの登録を実施
        object?.saveInBackground({ (error) in
            if error != nil {
                // 保存に失敗した場合の処理
            } else {
                // 保存に成功した場合の処理
            }
            // Queueから削除される
            self.state = .finished
        })

        // startメソッドが終了してもQueueから削除されない
    }
}

Tips

  • isCancelledプロパティの評価について
    startメソッド実行時など、キャンセルされたかどうかは常に頭にいれて実装する。
    キャンセルされている場合は、状態をfinishedに変更して終了させることが望ましい

  • isAsynchronousプロパティはiOSでも無視されていると思われる

    • 以下リファレンスの説明

      • オペレーションがそのタスクを非同期的に実行するかどうかを指定する
        • falseの場合 = queueに追加すると同時に呼び出し元のスレッド上でオペレーションが実行される
        • trueの場合 = オペレーションは別スレッド上で実行される
      • デフォルトの値はtrueである
      • macOS10.6以降はこの値は無視される
    • しかしながらiOSでもこの値は参照されていないため無視されていると思われる

    • operationを呼び出し元関数と同期したい場合は以下のメソッドを呼び出す

      Operation.swift
      func waiteUntileFinished()
      
      • 上記の関数をqueueに追加した直後でよびだす
      • queueから削除されるまでコードが止まるので、mainスレッド上では呼び出さないこと
  • オペレーションを直列(逐次)実行させたい場合は、queue側に実装されている maxConcurrentOperationCount プロパティの値を1にする

参考

16
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?