KVOとGCD

KVO

今回は、 KVOについて興味を持ったので調べてみることにした。 KVOは、RxSwiftを有効活用できる処理の一つです。

KVOとは、Key-value-Observingの略で、プロパティの値の変化を通知してくれる仕組みのこと。 Swiftで、KVOを行なった場合

private var observerContext = 0

override func viewDidLoad() {
    super.viewDidLoad()
   
    user.addObserver(self, forKeyPath: #keyPath(User.name), options: [.new, .initial], context: &observerContext)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if context == &observerContext {
        let newValue = change?[.newKey] as? String
        print("do something with newValue")
    } else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
    }
}

deinit {
    user.removeObserver(self, forKeyPath: #keyPath(User.name))
}

addObserver: notification queueのdispatch tableに、notification queueと queueに追加するブロック、オプションの通信名と送信者を含む登録を追加する。 ->キー値を監視して値の変更があった際に通知を受け取ることができる。 上記では、userの変更を監視している。

deinit:initが初期化処理を記述できるのに対して、クラスの後始末の処理を行うことができる。 上記では、removeObserverを行なっておりメモリリークを防いでいる。 Rxのdisposeに似ていることをしている。

この処理をRXswiftで行うと

override func viewDidLoad() {
    super.viewDidLoad()

    user.rx.observe(String.self, #keyPath(User.name))
        .subscribe(onNext: { newValue in
            print("do something with newValue")
        })
        .disposed(by: disposeBag)
}

となる。みてわかる通り簡単にまとめることができます。 また、RxCocoaのBehaviorRelayを使うことで

let user = BehaviorRelay(value: “Ryou”)

override func viewDidLoad() {
    super.viewDidLoad()
    user.accept(“Syou”)
}

user.subscribe(onNext: { newValue in
            print("do something with newValue")
        })
        .disposed(by: disposeBag)

と書くことができる。 プロパティ内の値が変わった時に処理を行うため、値の監視を行うことをKVOという。そして、RxSwiftを使うとこのKVOがより簡単に行うことができることがわかった。

GCD

GCDは、queueという「処理の入れ物」に対して処理を登録することで適切なスレッド上で処理が行われる。その際に、同期か非同期どちらの処理で行うかを選ぶことができる。 - 同期:キューに登録したスレッドが登録した処理が完了するのを待つ。     メソッドは、sync - 非同期:キューに処理を登録したスレッドが登録した処理が完了するのを待たない。     メソッドは、async

キューの種類

Dispatch Queue: 処理待ちタスクを追加するためのキュー。追加された順にタスクを処理側へ渡す役割を担う。タスクの処理は実行しない。 Dispatch Queueが処理側へタスクを引き渡す方式が2種類 - Serial:キュー内の処理が完了してから次の処理を実行させる。 - Concurrent:一度に複数の処理を実行することができる。 main queue:アプリの起動時にシステムによって自動的に作られるキューのことをいう。このキューは、メインスレッド上で順番に実行されるのでSerialキューとして考える。

基本的に、負荷の高い処理や終わるタイミングが未定の通信などの処理はメインスレッドでは行わない。 実際に、Dispatchを使った`同期``非同期`処理

func simpleQueues(){
    let queue = DispatchQueue(label: "com.ayakosayama.simpleQueue")
//syncを使っているので、同期処理を行なっている。
    queue.sync {
        for i in 0..<10{
            print("💜",i)
        }
    }
    
    for i in 100..<110{
        print("💛",i)
    }
}

//下記のように表示される
💜 0
💜 1
💜 2
💜 3
💜 4
💜 5
💜 6
💜 7
💜 8
💜 9
💛 100
💛 101
💛 102
💛 103
💛 104
💛 105
💛 106
💛 107
💛 108
💛 109

上記の場合、queueが直列に処理される同期処理のため💜 が先に表示され、💛 が後に表示されている。

func asyncQueues(){

        let queue = DispatchQueue(label: "com.ayakosayama.myqueue1")
        let queue2 = DispatchQueue(label: "com.ayakosayama.myqueue2")

        queue.async {
            for i in 0..<10{
                print("🔵",i)
            }
        }

        queue2.async {
            for i in 100..<110{
                print("🔴",i)
            }
        }

        for i in 1000..<1010{
            print("⚫️",i)
        }
    }

//下記のように表示される
🔵 0
🔴 100
⚫️ 1000
🔵 1
⚫️ 1001
🔴 101
⚫️ 1002
🔵 2
⚫️ 1003
🔵 3
⚫️ 1004
🔵 4
⚫️ 1005
🔵 5
⚫️ 1006
🔵 6
⚫️ 1007
🔵 7
⚫️ 1008
🔴 102
⚫️ 1009
🔵 8
🔴 103
🔵 9
🔴 104
🔴 105
🔴 106
🔴 107
🔴 108
🔴 109

この処理を行うと上記のようにそれぞれの処理を同時に行う。 このように、dispatchqueueで同期処理``非同期処理を切り替えることができる。

DIspatchでは、今回行なった非同期処理以外にも処理速度の制限などを行うことができる。