一、概述
NSNotification顾名思义就是通知的作用,一个对象通知另外一个对象,可以用来传递参数、通信等作用,与delegate的一对一不同,通知是多对多的。在一个对象中注册了通知,那么其他任意对象都可以来对这个对象发出通知。
二、NSNotificationCenter
每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信
任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么。其他感兴趣的对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时)收到这个通知
通知与代理的区别:通知是多对多的,代理是一对一的。如下图:
比如,键盘弹出的时候也会发送一条通知。
三、NSNotification
一个完整的通知一般包含3个属性:
1
2
3
|
-
(
NSString *
)
name
;
// 通知的名称
-
(
id
)
object
;
// 通知发布者(是谁要发布通知)
-
(
NSDictionary *
)
userInfo
;
// 一些额外的信息(通知发布者传递给通知接收者的信息内容)
|
创建一个通知(NSNotification)对象:
1
|
+
(
instancetype
)
notificationWithName
:
(
NSString *
)
aName
object
:
(
id
)
anObject
;
|
1
|
+
(
instancetype
)
notificationWithName
:
(
NSString *
)
aName
object
:
(
id
)
anObject
userInfo
:
(
NSDictionary *
)
aUserInfo
;
|
1
|
-
(
instancetype
)
initWithName
:
(
NSString *
)
name
object
:
(
id
)
object
userInfo
:
(
NSDictionary *
)
userInfo
;
|
实际上创建通知对象并不是必须的,更方便的是通过通知中心发送通知。
四、发布通知
通知中心(NSNotificationCenter)提供了相应的方法来帮助发布通知。
获得通知中心对象:
1
|
NSNotificationCenter *
center
=
[
NSNotificationCenter
defaultCenter
]
;
|
通知中心提供的发布通知的方法:
1
|
-
(
void
)
postNotification
:
(
NSNotification *
)
notification
;
|
上述为发布一个notification通知,可在notification对象中设置通知的名称、通知发布者、额外信息等。
1
|
-
(
void
)
postNotificationName
:
(
NSString *
)
aName
object
:
(
id
)
anObject
;
|
发布一个名称为aName的通知,anObject为这个通知的发布者。
1
|
-
(
void
)
postNotificationName
:
(
NSString *
)
aName
object
:
(
id
)
anObject
userInfo
:
(
NSDictionary *
)
aUserInfo
;
|
发布一个名称为aName的通知,anObject为这个通知的发布者,aUserInfo为额外信息。
当我们添加通知以后,我们就可以发出通知,也是通过NSNotificationCenter类的defaultCenter来获取到通知中心,然后通过postNotificationName来发出通知。这里需要注意的一点就是这样发出的通知是同步操作,也就是只有当发出的通知执行完毕以后才会继续执行接下去的代码:
1
2
3
|
-
(
void
)
getNotification
:
(
NSNotification *
)
info
{
NSDictionary *
dict
=
info
.
userInfo
;
}
|
这是发出通知以后会调用的方法,在多线程操作时,发出通知的对象和接收通知的对象需要处于同一个线程。
五、注册通知监听器
通知中心(NSNotificationCenter)提供了方法来注册一个监听通知的监听器(Observer)。
方法1:
1
|
-
(
void
)
addObserver
:
(
id
)
observer
selector
:
(
SEL
)
aSelector
name
:
(
NSString *
)
aName
object
:
(
id
)
anObject
;
|
observer:监听器,即谁要接收这个通知。
aSelector:收到通知后,回调监听器的这个方法,并且把通知对象当做参数传入,意思是收到通知后调用接收者中的哪个方法。
aName:通知的名称。如果为nil,那么无论通知的名称是什么,监听器都能收到这个通知,意思是监听哪个aName的通知。
anObject:通知发布者。如果为anObject和aName都为nil,监听器都收到所有的通知,意思是监听哪个对象发出的通知。
例如,监听输入框内容是否发生改变:
1
2
|
[
[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@
selector
(
textChange
)
name
:
UITextFieldTextDidChangeNotification
object
:
self
.
accountField
]
;
|
当输入框内容发生改变会自动发送通知,之后会调用我们自定义的textChange方法。
方法2:
1
2
|
-
(
id
)
addObserverForName
:
(
NSString *
)
name
object
:
(
id
)
obj
queue
:
(
NSOperationQueue *
)
queue
usingBlock
:
(
void
(
^
)
(
NSNotification *
note
)
)
block
;
|
name:通知的名称。
obj:通知发布者。
queue:决定了block在哪个操作队列中执行,如果传nil,默认在当前操作队列中同步执行。
block:收到对应的通知时,会回调这个block。
接下来我们通过一个小demo来看下具体用法(代码比较简单,不再解释,不过这个代码有小问题哦,还没有取消注册监听呢,取消方法后面再讲~):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//
// Person.h
// NotificationTest
//
// Created by 李峰峰 on 2017/1/27.
// Copyright © 2017年 李峰峰. All rights reserved.
//
#import <Foundation/Foundation.h>
@
interface
Person
:
NSObject
@
property
(
nonatomic
,
copy
)
NSString *
name
;
-
(
void
)
newsCome
:
(
NSNotification *
)
note
;
@
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//
// Person.m
// NotificationTest
//
// Created by 李峰峰 on 2017/1/27.
// Copyright © 2017年 李峰峰. All rights reserved.
//
#import "Person.h"
#import "NewsCompany.h"
@
implementation
Person
-
(
void
)
newsCome
:
(
NSNotification *
)
note
{
// 通知的发布者
NewsCompany *
obj
=
note
.
object
;
NSLog
(
@
"%@接收到了%@发出的通知,通知内容是:%@"
,
self
.
name
,
obj
.
name
,
note
.
userInfo
)
;
}
@
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//
// NewsCompany.h
// NotificationTest
//
// Created by 李峰峰 on 2017/1/27.
// Copyright © 2017年 李峰峰. All rights reserved.
//
#import <Foundation/Foundation.h>
@
interface
NewsCompany
:
NSObject
@
property
(
nonatomic
,
copy
)
NSString *
name
;
@
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//
// NewsCompany.m
// NotificationTest
//
// Created by 李峰峰 on 2017/1/27.
// Copyright © 2017年 李峰峰. All rights reserved.
//
#import "NewsCompany.h"
@
implementation
NewsCompany
@
end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
//
// main.m
// NotificationTest
//
// Created by 李峰峰 on 2017/1/27.
// Copyright © 2017年 李峰峰. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Person.h"
#import "NewsCompany.h"
int
main
(
int
argc
,
char
*
argv
[
]
)
{
@
autoreleasepool
{
// 1.初始化机构
NewsCompany *
tx
=
[
[
NewsCompany
alloc
]
init
]
;
tx
.
name
=
@
"腾讯新闻"
;
NewsCompany *
sina
=
[
[
NewsCompany
alloc
]
init
]
;
sina
.
name
=
@
"新浪新闻"
;
// 2.初始化3个人
Person *
zhangsan
=
[
[
Person
alloc
]
init
]
;
zhangsan
.
name
=
@
"张三"
;
Person *
lisi
=
[
[
Person
alloc
]
init
]
;
lisi
.
name
=
@
"李四"
;
Person *
wangwu
=
[
[
Person
alloc
]
init
]
;
wangwu
.
name
=
@
"王五"
;
NSNotificationCenter *
center
=
[
NSNotificationCenter
defaultCenter
]
;
// 3.添加监听器
// zhangsan只监听tx发出的junshi_news_come通知
// zhangsan
[
center
addObserver
:
zhangsan
selector
:
@
selector
(
newsCome
:
)
name
:
@
"junshi_news_come"
object
:
nil
]
;
[
center
addObserver
:
zhangsan
selector
:
@
selector
(
newsCome
:
)
name
:
@
"yule_news_come"
object
:
nil
]
;
// lisi
[
center
addObserver
:
lisi
selector
:
@
selector
(
newsCome
:
)
name
:
nil
object
:
tx
]
;
// wangwu
[
center
addObserver
:
wangwu
selector
:
@
selector
(
newsCome
:
)
name
:
nil
object
:
nil
]
;
// 4.发布新闻
// tx发布了一则叫做junshi_news_come的通知
[
center
postNotificationName
:
@
"junshi_news_come"
object
:
tx
userInfo
:
@
{
@
"title"
:
@
"伊拉克战争停止了"
,
@
"intro"
:
@
"伊拉克战争停止了.........."
}
]
;
// sina发布了一则叫做yule_news_come的通知
[
center
postNotificationName
:
@
"yule_news_come"
object
:
sina
userInfo
:
@
{
@
"title"
:
@
"川普出柜迎娶安倍"
,
@
"intro"
:
@
"川普出柜迎娶安倍.........."
}
]
;
return
0
;
}
}
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
2017
-
01
-
27
15
:
43
:
29.273
NotificationTest
[
2395
:
155782
]
张三接收到了腾讯新闻发出的通知
,通知内容是
:
{
intro
=
"伊拉克战争停止了.........."
;
title
=
"伊拉克战争停止了"
;
}
2017
-
01
-
27
15
:
43
:
29.275
NotificationTest
[
2395
:
155782
]
李四接收到了腾讯新闻发出的通知
,通知内容是
:
{
intro
=
"伊拉克战争停止了.........."
;
title
=
"伊拉克战争停止了"
;
}
2017
-
01
-
27
15
:
43
:
29.275
NotificationTest
[
2395
:
155782
]
王五接收到了腾讯新闻发出的通知
,通知内容是
:
{
intro
=
"伊拉克战争停止了.........."
;
title
=
"伊拉克战争停止了"
;
}
2017
-
01
-
27
15
:
43
:
29.276
NotificationTest
[
2395
:
155782
]
张三接收到了新浪新闻发出的通知
,通知内容是
:
{
intro
=
"川普出柜迎娶安倍.........."
;
title
=
"川普出柜迎娶安倍"
;
}
2017
-
01
-
27
15
:
43
:
29.277
NotificationTest
[
2395
:
155782
]
王五接收到了新浪新闻发出的通知
,通知内容是
:
{
intro
=
"川普出柜迎娶安倍.........."
;
title
=
"川普出柜迎娶安倍"
;
}
|
六、取消注册通知监听器
通知中心不会保留(retain)监听器对象,在通知中心注册过的对象,必须在该对象释放前取消注册。否则,当相应的通知再次出现时,通知中心仍然会向该监听器发送消息。因为相应的监听器对象已经被释放了,所以可能会导致应用崩溃。在上面我们的小demo中就没有取消注册监听器。
通知中心提供了相应的方法来取消注册监听器:
1
|
-
(
void
)
removeObserver
:
(
id
)
observer
;
|
1
|
-
(
void
)
removeObserver
:
(
id
)
observer
name
:
(
NSString *
)
aName
object
:
(
id
)
anObject
;
|
一般在监听器销毁之前取消注册(如在监听器中加入下列代码):
1
2
3
4
|
-
(
void
)
dealloc
{
//[super dealloc]; 非ARC中需要调用此句
[
[
NSNotificationCenter
defaultCenter
]
removeObserver
:
self
]
;
}
|
七、NSNotificationQueue
上面说到 NSNotificationCenter是一个同步操作,也就是只有当响应的通知的代码执行完毕以后,发出通知的对象的代码才会继续往下执行。而NSNotificationQueue则与之相反,其通知是异步发送的。
NSNotificationQueue:通知队列,用来管理多个通知的调用。通知队列通常以先进先出(FIFO)顺序维护通。NSNotificationQueue就像一个缓冲池把一个个通知放进池子中,使用特定方式通过NSNotificationCenter发送到相应的监听者。
创建通知队列方法:
1
|
-
(
instancetype
)
initWithNotificationCenter
:
(
NSNotificationCenter *
)
notificationCenter
NS_DESIGNATED_INITIALIZER
;
|
往队列加入通知(发送通知)方法:
1
2
3
|
-
(
void
)
enqueueNotification
:
(
NSNotification *
)
notification
postingStyle
:
(
NSPostingStyle
)
postingStyle
;
-
(
void
)
enqueueNotification
:
(
NSNotification *
)
notification
postingStyle
:
(
NSPostingStyle
)
postingStyle
coalesceMask
:
(
NSNotificationCoalescing
)
coalesceMask
forModes
:
(
nullable
NSArray
<
NSRunLoopMode
>
*
)
modes
;
|
移除队列中的通知方法:
1
|
-
(
void
)
dequeueNotificationsMatching
:
(
NSNotification *
)
notification
coalesceMask
:
(
NSUInteger
)
coalesceMask
;
|
发送方式:
NSPostingStyle包括三种类型:
1
2
3
4
5
|
typedef
NS_ENUM
(
NSUInteger
,
NSPostingStyle
)
{
NSPostWhenIdle
=
1
,
NSPostASAP
=
2
,
NSPostNow
=
3
}
;
|
NSPostWhenIdle:空闲发送通知,当运行循环处于等待或空闲状态时,发送通知,对于不重要的通知可以使用。
NSPostASAP:尽快发送通知,当前运行循环迭代完成时,通知将会被发送,有点类似没有延迟的定时器。
NSPostNow :同步发送通知,如果不使用合并通知 和postNotification:一样是同步通知。
合并通知:
NSNotificationCoalescing也包括三种类型:
1
2
3
4
5
|
typedef
NS_OPTIONS
(
NSUInteger
,
NSNotificationCoalescing
)
{
NSNotificationNoCoalescing
=
0
,
NSNotificationCoalescingOnName
=
1
,
NSNotificationCoalescingOnSender
=
2
}
;
|
NSNotificationNoCoalescing:不合并通知。
NSNotificationCoalescingOnName:合并相同名称的通知。
NSNotificationCoalescingOnSender:合并相同通知和同一对象的通知。
- 通过合并我们可以用来保证相同的通知只被发送一次。
forModes:(nullable NSArray<NSRunLoopMode> *)modes
可以使用不同的NSRunLoopMode
配合来发送通知,可以看出实际上NSNotificationQueue
与RunLoop
的机制以及运行循环有关系,通过NSNotificationQueue
队列来发送的通知和关联的RunLoop
运行机制来进行的。
八、NSNotificatinonCenter实现原理
NSNotificatinonCenter
是使用观察者模式来实现的用于跨层传递消息,用来降低耦合度。NSNotificatinonCenter
用来管理通知,将观察者注册到NSNotificatinonCenter
的通知调度表中,然后发送通知时利用标识符name
和object
识别出调度表中的观察者,然后调用相应的观察者的方法,即传递消息(在Objective-C中对象调用方法,就是传递消息,消息有name或者selector,可以接受参数,而且可能有返回值),如果是基于block
创建的通知就调用NSNotification
的block
。