LoginSignup
30
26

More than 3 years have passed since last update.

NotificationCenter.addObserverのドキュメントがわからない

Last updated at Posted at 2018-10-02

これはただの備忘録です。

NotificationCenter.addObserver

NotificationCenterは時々しか使わないので、いつもドキュメントを読みながら実装していますが、毎回、公式ドキュメントの英語に惑わされて、つい時間を浪費してしまいます。浪費ついでに、拙訳メモを書き残して、もう2度と翻訳しなくていいようにしたいと思います。
新しい知見はありません。
NotificationCenterの使い方に関しては既に良い記事があるので、こちらをオススメします。
Swift 3 以降の NotificationCenter の正しい使い方

問題

NotificationCenterの記事やブログはいくつもありますが、みんなが常にnilにしている引数があります。

NotificationCenter.default.addObserver( self,
                                        selector: #selector(hoge),
                                        name: .UIApplicationDidChangeStatusBarOrientation,
                                        object: nil )  // <--コレとか
NotificationCenter.default.addObserver( forName: .UIApplicationDidBecomeActive,
                                        object: nil,   // <--コレ
                                        queue: nil,    // キューは知ってる
                                        using: hoge ) 

この object と呼ばれている、Any? 型の引数は、何でしょうか?
nil でいいんでしょうか?

答え

この object は、受け取りたい通知を発行するオブジェクトです。
nil にすると、全部のオブジェクトが発行する通知を対象とします。
nil でいいんです

もう少し詳しく

どうやら、オブザーバーを登録する際に、どのオブジェクトからの通知を受け取るかを指定することができるようです。
NotificationCenterは、iOSからの通知を受け取るために使う事が多く、どのオブジェクトからと言われても困るので、nil を使いますが、複数の通知発行元から、通知を選択的に受け取る為に用意されているようです。
ついでに他の引数を私なりに説明してみると以下のようになります。

  • observer は、だいたい self です。つまり、だいたいUIViewControllerです。(NSObjectならなんでもいいです。) 通知が発行されたとき、それを受け取る関数を定義してあるオブジェクトを指定します。
  • selectorusing には、通知を受け取る関数を指定します。通知が投稿された時に呼ばれます。using にはブロックを指定することもできます。
  • name, forName は、対象にしたい通知の名前です。NSNotification.Name型です。extentionでNSNotification.Name型に自前のカスタム通知を定義させて使うこともできます。便利です。
  • queueは、通知を知らせるときに、受け取る関数をどのオペレーションキューで呼んで欲しいかを指定します。nil にしておくと、通知を発行したスレッドで同期的に呼ばれます。

簡単な定義なのに、なぜ毎回惑うのか?

うーん。英語が苦手なわけではないつもりなんですが。
Appleのドキュメントは基本的にはよくできていると思いますが、何故かこの説明は、いつも迷路に入り込んだような気持ちになって分からなくなってしまいます。

  • ネイティブな感じの英文だから
  • 用語の使い方が不統一
  • 単語が曖昧すぎて、いかようにも解釈できる

などでしょうか。
単に説明が間違っているのかもしれません。

以下に、ほぼ直訳と原文を並べておくので、お好きな方はどうぞ。

関数リファレンス

NotificationCenter.addObserver(_:selector:name:object:)

Apple公式ドキュメント

func addObserver(_ observer: Any,
                   selector aSelector: Selector,
                   name aName: NSNotification.Name?,
                   object anObject: Any?)
通知センターのディスパッチテーブルに、オブザーバーと通知セレクタと、任意の通知名と任意の送信者をもつエントリを追加する。
原文 Adds an entry to the notification center's dispatch table with an observer and a notification selector, and an optional notification name and sender.
observer
オブザーバーとして登録されるオブジェクト。
原文 Object registering as an observer.
aSelector
レシーバーがオブザーバーに送る、通知発行を知らせるメッセージを指定するセレクタ。aSelectorで指定したメソッドは、一つだけ引数(NSNotificationのインスタンス)をもたなければならない。
原文 Selector that specifies the message the receiver sends observer to notify it of the notification posting. The method specified by aSelector must have one and only one argument (an instance of NSNotification).
aName
オブザーバーを登録する対象の通知名。つまり、そのオブザーバーにはこの名前の通知だけが届けられる。
*nil*にしたら、通知センターはそのオブザーバーに通知するかどうかを決めるのに通知の名前を使わない。
原文 The name of the notification for which to register the observer; that is, only notifications with this name are delivered to the observer.
If you pass nil, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.
anObject
オブザーバーが受け取りたい通知を出すオブジェクト。つまり、この送信者から送られた通知だけがオブザーバーに通知される。
nilにすると、通知センターはオブザーバーに通知を出すかどうか判断するのに通知の送信者を使わない。
原文 The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer.
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer.
ディスカッション
アプリがiOS9以降、macOS 10.11以降を対象とするなら、deallocの中でオブザーバーを登録解除する必要はない。そうでないなら、オブザーバーやこのメソッドに渡したオブジェクトが解放される前に removeObserver(_:name:object:) を呼び出すべきである。
原文 If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method. Otherwise, you should call removeObserver(_:name:object:) before observer or any object passed to this method is deallocated.

NotificationCenter.addObserver(forName:0bject:queue:using:)

Apple公式ドキュメント

func addObserver(forName name: NSNotification.Name?,
                 object obj: Any?,
                 queue: OperationQueue?,
                 using block: @escaping (Notification) -> Void) -> NSObjectProtocol
通知センターのディスパッチテーブルに、通知キューと、そのキューに追加するブロックと、任意の通知名と任意の送信者をもつエントリを追加する。
原文 Adds an entry to the notification center's dispatch table that includes a notification queue and a block to add to the queue, and an optional notification name and sender.
name
オブザーバーを登録する対象となる通知の名前。つまり、この名前をもつ通知だけがオペレーションキューにブロックを追加するのに使われる。
nil にしたら、通知センターはオペレーションキューにブロックを追加するかどうかを決めるのに通知の名前を使わない。
原文 The name of the notification for which to register the observer; that is, only notifications with this name are used to add the block to the operation queue. If you pass nil, the notification center doesn’t use a notification’s name to decide whether to add the block to the operation queue.
obj
オブザーバーが受け取りたい通知を出すオブジェクト。つまり、この送信者から送られた通知だけがオブザーバーに通知される。
nil にすると、通知センターはオブザーバーに通知を出すかどうか判断するのに通知の送信者を使わない。
原文 The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer.
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer.
queue
block を追加するオペレーションキュー。
nil にすると、通知を投稿するスレッドと同期して実行される。
原文 The operation queue to which block should be added.
If you pass nil, the block is run synchronously on the posting thread.
block
通知を受け取ったときに実行されるブロック。
ブロックは通知センターにコピーされ、オブザーバーの登録が解除されるまで保持される。
ブロックは引数を一つとる。
notification
 通知。
原文 The block to be executed when the notification is received.
The block is copied by the notification center and (the copy) held until the observer registration is removed.
The block takes one argument:
notification
 The notification.
戻り値
オブザーバーとしてうごく透過オブジェクト。
原文 An opaque object to act as the observer.
ディスカッション
もし与えられた通知が一つより多いオブザーバーブロックを呼び出す場合、ブロック同士は非同期に呼び出される。(ただし指定されたキューまたは現在のスレッドで。)
以下のサンプルは、どうやってローカル変更通知の受け取りを登録するかを示す。
原文 If a given notification triggers more than one observer block, the blocks may all be executed concurrently with respect to one another (but on their given queue or on the current thread).
The following example shows how you can register to receive locale change notifications.
let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
self.localeChangeObserver = center.addObserverForName(NSCurrentLocaleDidChangeNotification, object: nil, queue: mainQueue) { (note) in
    print("The user's locale changed to: \(NSLocale.currentLocale().localeIdentifier)")
}
監視を登録解除するときは、このメソッドが返したオブジェクトを removeObserver(_:) に渡す。 removeObserver(_:) または removeObserver(_:name:object:) を、addObserver(forName:object:queue:using:) に渡したオブジェクトが解放される前に呼び出さなければならない。
原文 To unregister observations, you pass the object returned by this method to removeObserver(_:). You must invoke removeObserver(_:) or removeObserver(_:name:object:) before any object specified by addObserver(forName:object:queue:using:) is deallocated.
let center = NSNotificationCenter.defaultCenter()
center.removeObserver(self.localeChangeObserver)
もう一つの一般的なパターンは一回だけの通知を作ることで、監視ブロックの中でオブザーバーを削除すればよい。以下にサンプルを示す。
原文 Another common pattern is to create a one-time notification by removing the observer from within the observation block, as in the following example.
let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
var token: NSObjectProtocol?
token = center.addObserverForName("OneTimeNotification", object: nil, queue: mainQueue) { (note) in
    print("Received the notification!")
    center.removeObserver(token!)
}

おわりに

やっぱりネイティブな英語に、ついていけてないだけかも。
やれやれ。

30
26
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
30
26