- 前言 在OC中有一种消息广播机制,该机制可以向注册到Notification Center的Observe发送消息。其流程可以简单用下图表示。
- Observer注册到Notification Center
- Poster 发送消息到Notification Center
- Notification Center根据Poster发送的消息查找其维护的dispatch table
- 如果找到相对应的Observer需要的消息,那么将该消息广播至Observer
- Observer接受到消息,执行相对应的Action
- Notification Center将Observer移出 在消息通知机制中,需要关注三个角色Notification Center,Observer,Poster。
- Notification Center是一个消息通知机制,可以将消息广播至注册的observer。
- Observer 将其注册至Notification Center,可以接受相对应的消息,并且接收消息之后执行相对应的动作。
- Poster 将消息发送至Notification Center。 需要注意的是每一个running app 都有一个名为defaultCenter的notification center,你也可以创建一个新的notificaion center。此外,notification center可以传递消息在一个程序中,也可以在多个进程中传递消息。如果需要在多个进程中传递通知,可以使用NSDistributedNotificationCenter。
2. addObserver
向notification center中添加Observer有如下方法。
- (void)addObserver:(id)observer selector:(SEL)aSelector
name:(NSNotificationName)aName
object:(id)anObject;
observer:观察者对象
selector:接收消息后执行的方法,且该方法只有一个参数形如:-(void)acceptNotificationAction:(NSNotification *)noic;
name:消息名称,当为nil时,不作为搜索dispatch table的匹配属性。
obejct:接收哪个对象发送的消息,当为nil时,不作为搜索dispatch table的匹配属性。
- (id<NSObject>)addObserverForName:(NSNotificationName)name
object:(id)obj
queue:(NSOperationQueue *)queue
usingBlock:(void (^)(NSNotification *note))block;
name:消息名称,当为nil时,不作为搜索dispatch table的匹配属性。
object:接收哪个对象发送的消息,当为nil时,不作为搜索dispatch table的匹配属性。
queue:指定block的运行的queue,当queue为nil时,block默认同步运行在poster所在线程
usingBlock:执行的block,可以理解为observer block。
该方法返回一个不透明的对象,用来代表observer。Notification Center保持该对象一直到移除observer registration。
当你使用addObserverForName:(NSNotificationName)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block方法注册observer,并且只接收一次消息。可以使用如下的代码。
NSNotificationCenter * __weak center = [NSNotificationCenter defaultCenter];
id __block token = [center addObserverForName:@"OneTimeNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(@"Received the notification!");
[center removeObserver:token];
}];
3. removeObserver
将观察者从notification center中移除,并且不在接受消息通知,可以使用如下方法。
- (void)removeObserver:(id)observer
name:(NSNotificationName)aName
object:(id)anObject;
observe:观察者对象,dispacth table中移除观察者对象为observer的条目。
aName:消息名称 dispacth table中移除消息名称为aName的条目。如果为nil,则不作为搜索dispatch table的匹配属性。
anObject:发送消息的对象,dispacth table中移除发送消息的对象为anObject的条目。如果为nil,则不作为搜索dispatch table的匹配属性。
- (void)removeObserver:(id)observer;
observer:观察者对象,dispacth table中移除观察者对象为observer的条目。
在你removeObserver的时候,必须保证addObserve方法中的指定对象存在。当你使用(iOS 9.0及以后,macOS 10.11及以后)addObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject进行observer registration,你不需要使用removerObserver来unregister observer,系统会在下次对其发送消息时,会将其移除。 当你unregister observer时,如果你register observer时指定了消息名称以及消息的发送者,那么最好在unregister observer时最好使用下面的方法。
- (void)removeObserver:(id)observer
name:(NSNotificationName)aName
object:(id)anObject;
observe:观察者对象,dispacth table中移除观察者对象为observer的条目。
aName:消息名称 dispacth table中移除消息名称为aName的条目。如果为nil,则不作为搜索dispatch table的匹配属性。
anObject:发送消息的对象,dispacth table中移除发送消息的对象为anObject的条目。如果为nil,则不作为搜索dispatch table的匹配属性。
4. postNotification
发送消息至notification center,有三种方法。其中最简单的方法是
- (void)postNotification:(NSNotification *)notification;
第二种是指定消息名称,消息发送者,以及消息额外的信息。
- (void)postNotificationName:(NSNotificationName)aName
object:(id)anObject
userInfo:(NSDictionary *)aUserInfo;
最后一个相当于将第二种发送消息的方法的参数aUserInfo设置为nil。
- (void)postNotificationName:(NSNotificationName)aName
object:(id)anObject;
5. 代码实践
定义如下两个类NotificationA和NotificationB。代码如下: 类NotificationA定义如下:
//NotificationA.h
#ifndef NotificationA_h
#define NotificationA_h
@class NotificationB;
@interface NotificationA : NSObject
@property (weak) NotificationB *B;
- (void)removeObserver;
- (void)activeForNotification:(NSNotification *)notification;
- (void)postNotification;
- (void)addOberver;
@end
#endif /* NotificationA_h */
//NotificationA.m
#import <Foundation/Foundation.h>
#import "NotificationA.h"
#import "NotificationB.h"
@implementation NotificationA
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:@"A Send" object:self];
}
- (void)activeForNotification:(NSNotification *)notification{
NSLog(@"Accept notificaion from B");
}
- (void)addOberver{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(activeForNotification:) name:@"B Send" object:nil];
}
- (void)removeObserver{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"B Send" object:nil];
}
@end
类NotificationB定义如下:
// NotificationB.h
#ifndef NotificationB_h
#define NotificationB_h
@class NotificationA;
@interface NotificationB : NSObject
@property (weak) NotificationA *A;
- (void)removeObserver;
- (void)activeForNotification:(NSNotification *)notification;
- (void)postNotification;
- (void)addOberver;
@end
#endif /* NotificationB_h */
// NotificationB.m
@implementation NotificationB
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:@"B Send" object:self];
}
- (void)activeForNotification:(NSNotification *)notification{
NSLog(@"Accept notificaion from A");
}
- (void)addOberver{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(activeForNotification:) name:@"A Send" object:self.A];
}
- (void)removeObserver{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"A Send" object:nil];
}
@end
两个类都有下面的四个方法。
- (void)removeObserver;
- (void)activeForNotification:(NSNotification *)notification;
- (void)postNotification;
- (void)addOberver;
首先利用NotificationA对象作为Observer,NotificationB作为Nofication Poster。 main函数如下:
int main(int argc, const char * argv[]) {
NotificationA *A = [[NotificationA alloc] init];
NotificationB *B = [[NotificationB alloc] init];
A.B = B;
B.A = A;
[A addOberver];
[B postNotification];
return 0;
}
在类NotificationA中的addObserver中,register observer代码如下:name不为空,object为空。
- (void)addOberver{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(activeForNotification:) name:@"B Send" object:nil];
}
在类NotificationB中的postNotification中,name不为空,object也不为空。
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:@"A Send" object:self];
}
输出结果如下:可以看出,observer接收到了消息,执行了activeForNotification方法。
如果在类NotificationB中的postNotification中,name不为空,object也为空。
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:@"A Send" object:nil];
}
输出结果如下:可以看出,observer接收到了消息,执行了activeForNotification方法。
当register observer时,指定了notification name时,当post notification时,无论指定或不指定notificaion poster。observer都能接受到消息
那么接着我们addobserver时指定发送消息的对象,且不指定notification name。代码如下: 类NotificationA的中addObserver函数如下:
- (void)addOberver{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(activeForNotification:) name:nil object:self.B];
}
而此时类NotificationB的中postNotificaion函数如下:指定了object为self,notification name为nil。
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:“B Send” object:self];
}
输出结果如下:可以看出,observer接收到了消息,执行了activeForNotification方法。
但是如果notification name为nil呢,虽然此时编译器只会给一个警告(该参数是一个non-null argument),但是observer已经接受不到消息。
代码如下:
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:nil object:self];
}
输出结果如下:可以看出,observer没有接收到消息。
如果postNotification方法没有指定或者与指定的notificaion poster不对应,也无法接受到消息。
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:@"B Send" object:nil];
}
或
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:@"B Send" object:[[NSString alloc] init]];
}
当register observer时,指定了object,没有指定notification name。当post notification时,notification name必须指定, 且poster object必须与register obberver指定object的一样,这样observer才能接受到消息
我们可以使用removerObserver来unregister Observer, 当removerObserver时指定了notification name,无论addObserver时,object是否指定。在dispatch table中包含该notification name的条目都会被remove。 代码如下: 类NotificationA中addObserver代码如下:
- (void)addOberver{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(activeForNotification:) name:@"B Send" object:self.B];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(activeForNotification:) name:@"B Send" object:nil];
}
类NotificationA中removeObserver方法代码如下:
- (void)removeObserver{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"B Send" object:nil];
}
类NotificationB中postNotification方法代码如下:
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:@"B Send" object:[[NSString alloc] init]];
}
mian函数代码如下:
int main(int argc, const char * argv[]) {
NotificationA *A = [[NotificationA alloc] init];
NotificationB *B = [[NotificationB alloc] init];
A.B = B;
B.A = A;
[A addOberver];
[A removeObserve];
[B postNotification];
return 0;
}
结果如下:可以看出没有任何输出,说明removeObserver中指定了notification name,会将dispatch table中包含该notification name的条目都会被remove。
再看一个例子,当removerObserver时指定了poster object, 代码如下: 类NotificationA中addObserver代码如下:
- (void)addOberver{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(activeForNotification:) name:nil object:self.B];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(activeForNotification:) name:@"B Send" object:self.B];
}
类NotificationA中removeObserver方法代码如下:
- (void)removeObserver{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@“Test”object:self.B];
}
类NotificationB中postNotification方法代码如下:
- (void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:@"B Send" object:self]];
}
mian函数代码如下:
int main(int argc, const char * argv[]) {
NotificationA *A = [[NotificationA alloc] init];
NotificationB *B = [[NotificationB alloc] init];
A.B = B;
B.A = A;
[A addOberver];
[A removeObserve];
[B postNotification];
return 0;
}
结果如下:removeObserver没有生效,observer依然接收到了消息。
当removeObserver代码如下:
类NotificationA中removeObserver方法代码如下:
- (void)removeObserver{
[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:self.B];
}
结果如下:可以看出,observer被移除,且接受不到消息。那么removeObserver移除消息的原则是:指定了object和name,在dispatch table中搜索条目,这两项都一致的条目被移除。否则不移除。如果只指定name或object,则在dispatch table中寻找满足这一项的条目移除。
那Observer是不是也要在removerObserver中满足呢,将removeObserver代码改写如下:
- (void)removeObserver{
[[NSNotificationCenter defaultCenter] removeObserver:[[NSString alloc] init] name:nil object:self.B];
}
结果如下:可以看出,如果removeObserver方法指定observer对象,在dispatch table没有找到对应的条目,则也无法移除observer。
总结:在dispatch table中包含三个匹配项:
- Observer
- Notification Name
- Notification Poster 当register observer指定的这三项,remove Observer指定的三项满足才能在dispatch table中移除Observer,也就是Observer不能接受到消息。 当register observer指定的这三项,当你指定了notification name和notification 时时,postNotification指定的这两项需要保持一致。但只指定了notification 或 notification poster时,postnotification只需满足对应项一致,Observer就可以接受到消息。
6 总结
- notification center维持了一张dispatch table,其主要三项包括:Observer,Notification,Notification Poster。
- 当addObserver,指定了这三项时,removerObserver指定的这三项必须与addObserver指定的这三项一一对应才能从dispatch table移除。
- 当addObserver,指定了notification name和poster object不为空,则postNotification这两项都必须与其一致,Observer才能接受到消息。当只是指定了notificationname,poster object为空。则postNotification的notification name参数,必须与其一致,object参数则可以任意。当只指定了poster object。则postNotification的notification name(是一个non-null argument)必须指定,且poster obejct必须与其一致,Observer才能接受消息。