目录

关于NSNotificationCenter

通知(NSNotification)

一个完整的通知一般包含3个属性:

1
2
3
- (NSString*)name; // 通知的名称
- (id)object; // 通知发布者(是谁要发布通知)
- (NSDictionary*)userInfo; // 一些额外的具体信息

初始化一个通知(NSNotification)对象

1
2
3
+ (instancetype)notificationWithName:(NSString*)aName object:(id)anObject;
+ (instancetype)notificationWithName:(NSString*)aName object:(id)anObject userInfo:(NSDictionary*)aUserInfo;
- (instancetype)initWithName:(NSString*)name object:(id)object userInfo:(NSDictionary *)userInfo;

发布通知方法

通知中心(NSNotificationCenter)提供了相应的方法来帮助发布通知

如果发布通知的方法在那一条线程上,则监听通知的对象调用的方法在会在这个线程上运行

所以:

如果在异步线程发的通知,那么可以执行比较耗时的操作;

如果在主线程发的通知,那么就不可以执行比较耗时的操作

1
2
3
4
5
6
7
8
// 发布一个notification通知,可在notification对象中设置通知的名称、通知发布者、额外信息等
- (void)postNotification:(NSNotification*)notification;

// 发布一个名称为aName的通知,anObject为这个通知的发布者
- (void)postNotificationName:(NSString *)aName object:(id)anObject;

// 发布一个名称为aName的通知,anObject为这个通知的发布者,aUserInfo为一些额外信息
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;

注册通知监听器

通知中心(NSNotificationCenter)提供了相应的方法注册监听器(Observer)来监听通知的发布:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// observer:监听器,即谁要接收这个通知
// aSelector:收到通知后,回调监听器的这个方法,并且把通知对象当做参数传入
// aName:通知的名称。如果为nil,那么无论通知的名称是什么,监听器都能收到这个通知
// anObject:通知发布者。如果为anObject和aName都为nil,监听器都收到所有的通知
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;

// name:通知的名称
// obj:通知发布者
// block:收到对应的通知时,会回调这个block
// queue:决定了block在哪个操作队列中执行,如果传nil,默认在当前操作队列中同步执行
- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;

取消注册通知监听器

通知中心不会保留(retain)监听器对象,在通知中心注册过的对象,必须在该对象释放前取消注册。否则,当相应的通知再次出现时,通知中心仍然会向该监听器发送消息。因为相应的监听器对象已经被释放了,所以可能会导致应用崩溃

通知中心提供了相应的方法来取消注册监听器

1
2
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;

示例

一个朋友说看了我这篇还是该不懂还是不懂,感觉还是放个例子上来好一些,果然需求是技术进步的动力

首先要给通知起一个名字

最好用字符串常量,或者用宏也可以,这样可以保证发送和监听的是同一个通知

1
2
3
4
5
6
7
8
9
// 字符串常量
// 头文件中
extern NSString *const NotifationTest;

// 实现文件中
NSString *const NotifationTest = @"notificationTest";

// 或者直接用宏
#define NOTIFICATION_TEST @"notificationTest"

注册监听

给想要接受到通知的对象注册监听,这样就可以收到其他对象发的通知了,通知是可以重复注册的,每次多一次注册就会多收到一次通知,所以尽量在只执行一次的方法中注册,比如控制器的viewDidLoad方法中

1
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fuck) name:NotifationTest object:nil];

发送通知后,监听者会受到通知然后调用fuck方法 ! 😄

发送通知

发送通知就很简单了,想法通知的时候只需要post一下就好了

1
[[NSNotificationCenter defaultCenter] postNotificationName:NotifationTest object:nil];

移除通知

监听器被销毁了一定要记得取消注册…

1
2
3
4
5
6
// 一般在监听器销毁之前取消注册(如在监听器中加入下列代码):
- (void)dealloc
{
    [super dealloc];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

当然可能由于种种原因(内存泄漏),监听器该销毁的时候并没有被销毁导致的dealloc方法没有被调用(出现这个问题的地方大多是控制器),这样的话,也不会取消注册通知的,这时候有可能第二次创建这个控制器的时候重复注册,所以还有一种比较稳妥的方法是这样

1
2
3
4
5
6
7
8
9
- (void)viewWillAppear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fuck) name:NotifationTest object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

在控制器将要出现在屏幕的时候注册通知,控制器将要消失的时候取消注册,一般情况下这样也就够了,但是这个在离开屏幕时是收不到通知的,所以如果有需求是不在当前屏幕也要接收通知的话,这个方式就不可以了..

UIDevice 通知

UIDevice类提供了一个单例对象,它代表着设备,通过它可以获得一些设备相关的信息,比如电池电量值(batteryLevel)、电池状态(batteryState)、设备的类型(model,比如iPod、iPhone等)、设备的系统(systemVersion)

通过[UIDevice currentDevice]可以获取这个单粒对象

UIDevice 对象会不间断地发布一些通知,下列是 UIDevice 对象所发布通知的名称常量:

  1. UIDeviceOrientationDidChangeNotification // 设备旋转
  2. UIDeviceBatteryStateDidChangeNotification // 电池状态
  3. UIDeviceBatteryLevelDidChangeNotification // 电池电量
  4. UIDeviceProximityStateDidChangeNotification // 近距离传感器(比如设备贴近了使用者的脸部)

最后来一发鸡汤

实践是最好的老师,但是智者还能从其他地方有所收货。