NSNotificationCenterとセレクター記述のこと

iOSでは段を積んだようにメッセージキューが用意されています。そのアプリケーションワイドのやつを触るのがNSNotificationCenter。シングルViewアプリでUITextFieldを一個貼っただけのものを作ります。

import UIKit
class ViewController: UIViewController {
    @IBOutlet weak var fistNameTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 初期値の取り出し
        let userDefaults = NSUserDefaults.standardUserDefaults()
        self.fistNameTextField.text = userDefaults.objectForKey("firstName") as? String
        // イベント通知先を登録
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "saveName:", 
            name: UIApplicationDidEnterBackgroundNotification, object: nil)
    }

    // 通知を受け取る
    func saveName(notification: NSNotification?) {
        let userDefaults = NSUserDefaults.standardUserDefaults()
        userDefaults.setObject(fistNameTextField.text, forKey: "firstName")
        userDefaults.synchronize()
    }    
}

シンプルだと以上。NSNotificationCenter.defaultCenter()でシングルトンを取ったあと、NSNotificationCenter#addObserver()でイベント通知先を登録します。引数はそれぞれ通知受領オブジェクト、通知受領メソッド、通知イベント名。最後に具体的なオブジェクトをいれてあげるとそこからの通知しか受け取らないとリファレンスには書いてありました。nilだとフィルタされずにどこから飛んできたものも受け取ることになります。

さてSwiftでもObject-Cから受け継ぐセレクターという概念があり、ここでは適当に名付けた「saveName」を文字列で渡してあげたんだけど、これ気持ち悪いねー。関数そのまま渡せればいいのに。もし文字列を間違ったら永遠に通知がこないことになりますが、コンパイラはまったく検知しない。UIApplicationDidEnterBackgroundNotificationがアプリがバックグラウンドに行った時に発生するイベント名。こういうのをドキュメントから探してきたり、自分でNSNotificationCenter#postNotification()するのですね。

通知を受け取る側はNSNotificationを受け取れる引数リストもった関数を書きます。ここ、伝統的なObjective-Cな人たちは、-(void) saveName: (id)sender { ... } と書いちゃうみたい。idは型なしポインタ?で、SwiftではAnyObject?となり要はなんでも受け取れるという。実際にはガッチリNSNotificationが飛んできますからちゃんと書いた方がいいと思います。NSNotificationをOption型にしておけば手動で呼ぶ時にはnil投げちゃえばよいです。