通知中心:实现了一对多的消息传递,可以实现跨页面传递。
cocoa消息机制算是同步的,观察者只要向消息中心注册,即可接受其他对象发送来的消息,消息发送者和消息接受者两者可以互相一无所知,完全解耦。这种消息机制可以用应用于任意时间和任何对象,观察者可以有多个。所以消息具有广播的性质,注意:观察者向消息中心注册以后,在不需要接受消息时需要向中心注销,属于典型的观察者模式。
消息通知中重要的两个类:
1)NSNotificationCenter:实现NSNotificationCenter的原理是一个观察者模式,获得NSNotificationCenter的方法只有一种,那就是[NSNotificationCenter defaultCenter],通过调用静态方法defaultCenter就可以获取这个通知中心的对象了。NSNotificationCenter 是一个单例模式,而这个通知中心的对象会一直存在于一个应用的生命周期。
2)NSNotification:这是消息携带的载体,通过它,可以把消息内容传递给观察者。
NSNotificationCenter的主要方法有以下几种:
@property (class, readonly, strong) NSNotificationCenter *defaultCenter;
- (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
//。1.notificationObserver不能为nil
// 2.notificationSelector回调方法有且只有一个参数(NSNotifcation对象)。
//。3.监听同一条通知的多个观察者,在通知到达时,它们执行回调的顺序是不确定的,所以我们不能去假设操作的执行会按照添加观察者的顺序来执行。
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
// The return value is retained by the system, and should be held onto by the caller in
// order to remove the observer with removeObserver: later, to stop observation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
其中最后一个方法返回NSObserver对象,在使用此方法添加观察者的时候,需要注意循环引用和remove的方式。每种方法都在不同系统上的表现也不尽相同。
一、添加observer的方法
1.SEL方式
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
1
此方法中的observer是弱引用,直接使用self等不会造成强引用。
提示:如果同一个通知连着添加n次,那SEL的方法会调用n次,所以要确保addOnserver和removeObserver成对的出现
2.Block方式
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
// The return value is retained by the system, and should be held onto by the caller in
// order to remove the observer with removeObserver: later, to stop observation.
1
2
3
注意:1)这个方法会返回一个NSObserver对象,和这个对象被系统强持有,调用者需要持有这个对象,拥于停止通知移除观察者时使用。这种方式添加的通知,如果不持有这个对象,是无法彻底销毁这个通知的,具体做法如下:
@property (nonatomic, strong) id observer;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 添加通知观察者
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
}];
}
- (void)dealloc {
if (_observer) {
[[NSNotificationCenter defaultCenter] removeObserver:_observer];
_observer = nil;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2)这个方法的block会强持有对象,所以在block中的成员变量和属性,需要弱引用,如果没有弱引用,则需要在delloc函数之前对通知进行移除,否则不会执行delloc函数。
@property (nonatomic, strong) id observer;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 需要进行弱引用
__weak typeof(self) weakSelf = self;
// 添加通知观察者
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
weakSelf.label.backgroundColor = [UIColor redColor];
}];
}
- (void)dealloc {
if (_observer) {
[[NSNotificationCenter defaultCenter] removeObserver:_observer];
_observer = nil;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
回调方法
-(void)notificationFirst:(NSNotification *)notification{
NSString *name=[notification name];
NSString *object=[notification object];
NSLog(@"名称:%@----对象:%@",name,object);
}
-(void)notificationSecond:(NSNotification *)notification{
NSString *name=[notification name];
NSString *object=[notification object];
NSDictionary *dict=[notification userInfo];
NSLog(@"名称:%@----对象:%@",name,object);
NSLog(@"获取的值:%@",[dict objectForKey:@"key"]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
二、发送通知的方法
发送通知主要是以下三种方式:
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObjectl
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
1
2
3
注意:发送通知时,所在的线程,如果在子线程发送通知,那么通知的接收也是在子线程。
发送通知中的object参数和注册时使用的方法关联。
举个例子:
// 注册
self.name = @"Tom";
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test1) name:@"lala" object:_name];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test2) name:@"lala" object:nil];
1
2
3
4
一个object是nil,一个是_name。以下以两种不同的方式去发送通知:
第一种
[[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:nil];
1
这种方式发送通知的时候,只有test2方法会被调用。
第二种:
[[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:_name];
1
这种方法发生通知的时候,test1和test2都会被调用。
_name = @"lala";
[[NSNotificationCenter defaultCenter] postNotifcationName:"lala" object:_name];
1
2
此时只有test2被调用,因为_name的地址被更改了,所以通知匹配不上了,导致test1无法被识别到了。以上可以说明如果添加通知的时候传入了object参数,那么发送通知时,就会匹配name和object两个条件。如果没传object参数,则只匹配name。
移除的时候一样
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"lala" object:_name];
1
test1被移除了
[[NSNotificationCenter defaultCenter] removeObserve: self name:@"lala" object:nil];
1
test1和test2都被移除掉了。
如果使用
[[NSNotificationCenter defaultCenter] removeObserver:self];
1
效果一样,不同之处在于这个方法不止会移除掉自己添加的通知方法,同时也会移除掉系统的通知方法,所以除非是这个对象要被释放掉,不要轻易使用这种方法进行移除通知。
总结:
1.如果添加通知的时候传入了object参数,那么发送通知时,就会匹配name和object两个条件。如果没传object参数,则只匹配name。
2.如果中途修改object对象,则对应有object发送对象的通知方式将会失效。
使用步骤:
1.在需要监听的部分注册监听器
2.实现通知监听器的回调函数
3.在监听器对象销毁前删除通知监听器
4.如果有通知需要发送,使用NSNotificationCenter发送通知。
我们在监听器回调函数中修改UI不会产生任何问题,但当通知是在其他线程中发送的,监听器回调函数中修改UI不会产生任何问题,但当通知是在其他线程中发送的,监听器回调函数很有可能就是在发送通知的那个线程中执行,我们知道UI的更新必须在主线程中执行,这个时候就需要注意,如果通知监听器的回调函数需要更新UI的代码,需要使用GCD放在主线程中执行,代码入下:
//NextViewController发送通知的代码修改为如下代码:
- (void)completeButtonClickedHandler
{
//使用GCD获取一个非主线程的线程用于发送通知
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"inputTextValueChangedNotification" object:nil userInfo:@{@"inputText": self.textField.text}];
});
[self dismissViewControllerAnimated:YES completion:nil];
}
//ViewController通知监听器的回调函数修改为如下代码:
- (void)inputTextValueChangedNotificationHandler:(NSNotification*)notification
{
//使用GCD获取主线程并更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.label.text = notification.userInfo[@"inputText"];
});
//如果不在主线程更新UI很有可能无法正确执行
//self.label.text = notification.userInfo[@"inputText"];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
很多时候我们使用的是第三方框架发送的通知,或是系统提供的通知,我们无法预知这些通知是否是在主线程中发送的,为了安全起见最好在需要更新UI时使用GCD将更新的逻辑放入主线程执行。
————————————————
版权声明:本文为CSDN博主「ACERROR」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36928687/article/details/108857352
网友评论