KVO(Key-Value Obeserving)でcontextを設定する


KVOを使う場合に「よく分からないけれど、とりあえずcontextにはnilを渡しておけばいいいんでしょ！？」と考えて機械的にnil設定している人は実は意外に多いのではないでしょうか？実際、多くの場合、contextは使わなくても問題ありません。しかし、キー値監視するスーパークラスにおいても同じkey/valueを監視しているようなケースにおいてはcontext情報を使ってイベントを識別する必要があります。
下記は監視対象モデルクラスがPerson、「contextを設定して」キー値監視するクラスがViewControllerである場合の例です。後半部分でコード中の各注釈に関して補足説明します。

## サンプルコード
**環境**
- Xcode 7.1.1
- Swift 2.1
- iOS 9.1

```swift:Person.swift
import Foundation

class Person: NSObject {
    
    private(set) dynamic var name: String
    dynamic var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}
```

```swift:ViewController.swift(一部抜粋)
import UIKit

// (1) プライベートなグローバル変数を用意
private var kViewControllerContext: UInt8 = 0

class ViewController: UIViewController {
    
    @IBOutlet weak var age: UITextField!
    
    lazy var jonathan = Person(name: "Jonathan", age: 20)
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

		 // (2) コンテキストとしてアドレスを渡す
        let opts: NSKeyValueObservingOptions = [.New, .Old]
        jonathan.addObserver(self, forKeyPath: "age", options: opts, context: &kViewControllerContext)
    }
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        jonathan.removeObserver(self, forKeyPath: "age", context: &kViewControllerContext)
    }
    
    
    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        
        // (3-1) アドレスを使って自身へのイベントであるか識別する
        if context == &kViewControllerContext {
            print("self: \(self), keypath: \(keyPath), object: \(object), change: \(change), context; \(context)")
            
            // (4) おまけ
            let pointer = UnsafeMutablePointer<UInt8>(context)
            let value = pointer.memory
            print("pointer: \(pointer)")
            print("value: \(value)")
        
        // (3-2) 自身以外のイベントについてはスーパークラスへ        
        } else {
            super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
        }
    }
    
    
    @IBAction func changeValue(sender: AnyObject) {
        if let number = Int(self.age.text!) where number >= 0 {
            print("number: \(number)")
            self.jonathan.age = number
        }
    }

}
```

## 補足説明
サンプルコードにおいて特に重要なのは(1)と(3-2)です。

**(1) プライベートなグローバル変数を用意**
context識別にはアドレスを使います。そのアドレスが割り当てられるプライベートなグローバル変数を用意します。通常、SwiftではUIntよりもIntを使用することが推奨されていますが、今回のケースではアドレスを使うことが目的であるため、サイズの明確化及び無駄なサイズを使わないようにするためにUInt8を使っています。

**(2) コンテキストとしてアドレスを渡す**
(1)において用意した変数のアドレスをコンテキストとして渡します。

**(3-1) アドレスを使って自身へのイベントであるか識別する**
自身に対するイベントであるかを識別した上で、必要な処理を実装します。

**(3-2) 自身以外のイベントについてはスーパークラスへ**
自身に対するイベントでない場合は、スーパークラスに対するイベントである可能性があるため、スーパークラスのメソッドを呼び出します。

**(4) おまけ**
context識別にアドレスを利用するのがポイントであるため、その中身である値自体にはあまり意味がありませんが、下記のようにして値を取り出すことができます。

```swift:C-APIによる値取り出し
let pointer = UnsafeMutablePointer<UInt8>(context)
let value = pointer.memory
print("pointer: \(pointer)")
print("value: \(value)")
```

## 最後に
どんな場合においてもcontextを設定する必要があるわけではありません。設定する必要がないことが明確な場合は、contextにはnilを渡すだけで十分だと思います。

\[参考URL\]
[Adopting Cocoa Design Patterns](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html)