- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject;
Declaration(描述):Adds an entry to the notification center's dispatch table with an observer and a notification selector, and an optional notification name and sender.(使用观察者和通知选择器以及可选的通知名称和发送者,将一个条目添加到通知中心的调度表中。)
Parameters(参数):
observerObject :registering as an observer(观察者 :对象注册为观察者。)
aSelector :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).:(选择器 :选择器,它指定接收者发送给观察者的消息,以通知其通知发布。 由aSelector指定的方法必须只有一个参数(一个NSNotification实例))
aName: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.(一个名字: 注册观察者的通知的名称; 也就是说,只有具有此名称的通知才传递给观察者。如果您输入nil,则通知中心不会使用通知名称来决定是否将其传递给观察者。)
anObject :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.(观察者想要接收其通知的对象;也就是说,只有此发送者发送的通知才传递给观察者。如果您通过nil,通知中心将不使用通知的发送者来决定是否将其传递给观察者。)
- (id
)addObserverForName:(NSNotificationName)name object:(id)obj queue:(NSOperationQueue*)queue usingBlock:(void(^)(NSNotification*note))block; Declaration: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: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.(注册观察者的通知的名称;也就是说,仅使用具有该名称的通知将块添加到操作队列中。如果您通过nil,通知中心将不使用通知名称来决定是否将块添加到操作队列中。)
obj: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 observ(观察者想要接收其通知的对象;也就是说,只有此发送者发送的通知才传递给观察者。如果您通过nil,通知中心将不使用通知的发送者来决定是否将其传递给观察者。)
queue:The operation queue to which block should be added.If you pass nil, the block is run synchronously on the posting thread.(block应该添加到的操作队列。如果通过nil,则该块将在发布线程上同步运行。:决定block在哪个线程执行,nil:在发布通知的线程中执行,分析:如果上面的是:queue 为 nil,那么block里面的线程是由发送通知的线程决定,那么如果block里面是子线程我们就无法刷新UI了,解决办法是把 nil 改为 [NSOperationQueue mainQueue],不管发送通知的线程是什么,block里面都是主线程)
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.:(收到通知时要执行的块。该块由通知中心复制并保留(副本),直到删除观察者注册为止。该块带回的一个参数:notification)
Return Value An opaque object to act as the observer.(一个不透明的对象,充当观察者。)
- (void)postNotificationName:(NSNotificationName)aName object:(id)anObject userInfo:(NSDictionary*)aUserInfo;
Declaration:Creates a notification with a given name, sender, and information and posts it to the notification center.(创建具有给定名称,发件人和信息的通知,并将其发布到通知中心。)
Parameters(参数):
aName: The name of the notification.(通知的名称。)
anObject:The object posting the notification.(发布通知的对象。)
aUserInfo:Optional information about the the notification.(关于通知的可选信息。)
- (void)postNotificationName:(NSNotificationName)aName object:(id)anObject;
Declaration:Creates a notification with a given name and sender and posts it to the notification center.(创建具有给定名称和发件人的通知,并将其发布到通知中心。)
- (void)postNotification:(NSNotification*)notification;
Declaration:(将给定的通知发布到通知中心。)
Parameters:notification The notification to post.(要发布的通知。)
- (void)removeObserver:(id)observer name:(NSNotificationName)aName object:(id)anObject;
Declaration:Removes matching entries from the notification center's dispatch table.(从通知中心的调度表中删除匹配的条目。)
observer :Observer to remove from the dispatch table. Specify an observer to remove only entries for this observer.(观察者从调度表中删除。指定观察者以仅删除该观察者的条目。)
aName:Name of the notification to remove from dispatch table. Specify a notification name to remove only entries that specify this notification name. When nil, the receiver does not use notification names as criteria for removal.(要从调度表中删除的通知的名称。指定通知名称以仅删除指定此通知名称的条目。如果为nil,则接收方不使用通知名称作为删除标准。)
anObject:Sender to remove from the dispatch table. Specify a notification sender to remove only entries that specify this sender. When nil, the receiver does not use notification senders as criteria for removal.(发件人从调度表中删除。指定通知发件人以仅删除指定该发件人的条目。nil设为时,接收者不使用通知发送者作为删除标准。)
- (void)removeObserver:(id)observer;
Declaration:Removes all entries specifying a given observer from the notification center's dispatch table.(从通知中心的分发表中删除所有指定给定观察者的条目。)
Notification :
一個通知分成幾個部分
object: 發送者,是誰送出了這個通知
name: 這個通知叫做什麼名字
user info: 這個通知還帶了哪些額外資訊
所以,當我們想要監聽某個通知的時候,便是指定要收聽由誰所發出、哪個名字的通知,並且指定負責處理通知的 selector,以前面處理 locale 改變的例子來看,我們就會寫出這樣的 code:
意思就是,我們要指定 name 為 NSCurrentLocaleDidChangeNotification 的通知,交由 localeDidChange: 處理。在這邊 object 設定為 nil,代表不管是哪個物件送出的,只要符合 NSCurrentLocaleDidChangeNotification 的通知,我們統統都處理。
每個通知當中,還可能會有額外的資訊,會夾帶在 NSNotification 物件的 userInfo 屬性中,userInfo 是一個 NSDictionary。
如果我們在 -addObserver:selector:name:object: 裡頭,把 name 指定成 nil,就代表我們想要訂閱所有的通知,通常不太會有這種情境,不過有時候你想要知道系統內部發生了什麼事情,可以用這種方式試試看。
當我們不需要繼續訂閱某項通知的時候,記得對 Notification Center 呼叫 -removeObserver:,以上面的程式為例,我們在 add observer 的時候傳入了 self,在 remove observer 的時候,就要傳入 self。我們通常在 dealloc 的時候停止訂閱。
在 iOS 4 與 Mac OS X 10.6 之後,我們可以使用 -addObserverForName:object:queue:usingBlock: 這組使用 block 語法的 API 訂閱通知,由於是傳入 block,所以我們就不必另外準備一個 selector,可以將處理 notification 的程式與 add observer 的這段呼叫寫在一起。而 remove observer 的寫法也會不太一樣: -addObserverForName:object:queue:usingBlock: 會回傳一個 observer 物件,我們想要停止訂閱通知的時候,是對 -removeObserver: 傳入之前拿到的 observer 物件。範例如下。
Add observer 的時候:
Notification 與 Threading
當我們訂閱某個 notification 之後,我們並不能夠保證負責處理 notification 的 selector 或 block 會在哪個 thread 執行:這個 notification 是在哪條 thread 送出的,負責接受的 selector 或是 block,就會在哪條 thread 執行。
在慣例上,絕大多數的 notification 都會在 main thread 送出,之所以說「絕大多數」,就是因為有例外:像是在 iOS 上,如果我們接上耳機、拔除耳機,或是將音樂透過 AirPlay 送到 Apple TV 的時候,系統會透過 AVAudioSessionRouteChangeNotification 告訴我們音訊輸出設備改變了1,這個通知就會發生在背景,而不是 main thread。
不過,當我們在撰寫自己的程式,要發送 notification 的時候,為了考慮其他開發者會預期在 main thread 收到 notification,所以我們也就在 main thread 發送 notification,像是透過 GCD,把 postNotification 的呼叫送到 dispatch_get_main_queue() 上。
不管添加通知在主线程还是子线程,接收通知的方法所在的线程是由发送通知的线程决定的。
最后:iOS 9 以后通知不再需要手动移除
通知NSNotification在注册者被回收时需要手动移除,是一直以来的使用准则。原因是在MRC时代,通知中心持有的是注册者的unsafe_unretained指针,在注册者被回收时若不对通知进行手动移除,则指针指向被回收的内存区域,成为野指针。这时再发送通知,便会造成crash。而在iOS 9以后,通知中心持有的是注册者的weak指针,这时即使不对通知进行手动移除,指针也会在注册者被回收后自动置空。我们知道,向空指针发送消息是不会有问题的
但是有一个例外。如果用- (id
)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));这个API来注册通知,可以直接传入block类型参数。使用这个API会导致注册者被系统retain,因此仍然需要像以前一样手动移除通知,同时这个block类型参数也需注意避免循环引用。