一、添加通知監(jiān)聽者的方式
- (void)addObserver:(id)observer selector:(SEL)aSelector 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 ;
以上是添加通知監(jiān)聽者的兩種方式
1. 添加監(jiān)聽者時的name與object
兩種方式添加監(jiān)聽者傳入的參數(shù)都包含name和object醋火,這兩個參數(shù)都可以為空昧廷,關(guān)于這兩個參數(shù)主要有以下四種情況:
1.1 若是name和object同時為空則可以監(jiān)聽程序中所有的通知
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receviedNoti:) name:nil object:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:nil object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"block---%@",note);
}];
}
- (void)receviedNoti:(NSNotification *)noti {
NSLog(@"selector---%@",noti);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_one" object:nil userInfo:@{@"ncKey":@"ncValue"}];
}
添加了兩個監(jiān)聽者被碗,name與object都為空
2018-11-22 16:52:46.457900+0800 Nunca[50654:7785885] selector---NSConcreteNotification 0x60000025ae20 {name = UIViewAnimationDidStopNotification; object = <UIViewAnimationState: 0x7f9f76603fe0>; userInfo = {
delegate = "<UIViewAnimationBlockDelegate: 0x60400047ddc0>";
name = "";
}}
2018-11-22 16:52:46.458099+0800 Nunca[50654:7785885] block---NSConcreteNotification 0x60000025ae20 {name = UIViewAnimationDidStopNotification; object = <UIViewAnimationState: 0x7f9f76603fe0>; userInfo = {
delegate = "<UIViewAnimationBlockDelegate: 0x60400047ddc0>";
name = "";
}}
2018-11-22 16:53:03.266696+0800 Nunca[50654:7785885] selector---NSConcreteNotification 0x60400025e2d0 {name = _UIWindowSystemGestureStateChangedNotification; object = <UIWindow: 0x7f9f765074d0; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x6040002500b0>; layer = <UIWindowLayer: 0x60400022a260>>; userInfo = {
"_UIWindowSystemGestureCancellingTouchesUserInfoKey" = 0;
}}
2018-11-22 16:53:03.266918+0800 Nunca[50654:7785885] block---NSConcreteNotification 0x60400025e2d0 {name = _UIWindowSystemGestureStateChangedNotification; object = <UIWindow: 0x7f9f765074d0; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x6040002500b0>; layer = <UIWindowLayer: 0x60400022a260>>; userInfo = {
"_UIWindowSystemGestureCancellingTouchesUserInfoKey" = 0;
}}
2018-11-22 16:53:03.267779+0800 Nunca[50654:7785885] selector---NSConcreteNotification 0x60400025d610 {name = noti_one; userInfo = {
ncKey = ncValue;
}}
2018-11-22 16:53:03.267996+0800 Nunca[50654:7785885] block---NSConcreteNotification 0x60400025d610 {name = noti_one; userInfo = {
ncKey = ncValue;
}}
監(jiān)聽到了程序中所有發(fā)出來的通知奏属,上面截取了一部分伶丐,最后打印的是點擊事件中自己添加的悼做。
1.2 若是name不為空、object為空則可以監(jiān)聽所有發(fā)送時通知名稱與name相同的通知
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receviedNoti:) name:@"nc_noti_one" object:nil];
[[NSNotificationCenter defaultCenter] addObserverForName:@"nc_noti_one" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"block---%@",note);
}];
}
- (void)receviedNoti:(NSNotification *)noti {
NSLog(@"selector---%@",noti);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[[NSNotificationCenter defaultCenter] postNotificationName:@"nc_noti_one" object:nil userInfo:@{@"ncKey_1":@"ncValue_1"}];
[[NSNotificationCenter defaultCenter] postNotificationName:@"nc_noti_one" object:@"123" userInfo:@{@"ncKey_2":@"ncValue_2"}];
}
添加兩個監(jiān)聽者哗魂,監(jiān)聽的name相同贿堰,object都為空,在點擊屏幕時發(fā)送兩條name相同的通知啡彬,但是一條object為空羹与,一條不為空
2018-11-22 17:05:38.920806+0800 Nunca[50859:7801142] selector---NSConcreteNotification 0x604000253470 {name = nc_noti_one; userInfo = {
"ncKey_1" = "ncValue_1";
}}
2018-11-22 17:05:38.921132+0800 Nunca[50859:7801142] block---NSConcreteNotification 0x604000253470 {name = nc_noti_one; userInfo = {
"ncKey_1" = "ncValue_1";
}}
2018-11-22 17:05:38.921327+0800 Nunca[50859:7801142] selector---NSConcreteNotification 0x600000248610 {name = nc_noti_one; object = 123; userInfo = {
"ncKey_2" = "ncValue_2";
}}
2018-11-22 17:05:38.921576+0800 Nunca[50859:7801142] block---NSConcreteNotification 0x600000248610 {name = nc_noti_one; object = 123; userInfo = {
"ncKey_2" = "ncValue_2";
}}
監(jiān)聽者name不為空、object為空時庶灿,不管發(fā)送通知時有沒有傳入object纵搁,只要name對上了,就能被成功監(jiān)聽往踢。
1.3 若是name為空腾誉、object不為空則可以監(jiān)聽所有發(fā)送通知時傳入的object與監(jiān)聽傳入的object為同一個對象的通知
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receviedNoti:) name:nil object:self];
[[NSNotificationCenter defaultCenter] addObserverForName:nil object:@"123" queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"block---%@",note);
}];
}
- (void)receviedNoti:(NSNotification *)noti {
NSLog(@"selector---%@",noti);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_name1" object:self userInfo:@{@"ncKey_1":@"ncValue_1"}];
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_name2" object:@"123" userInfo:@{@"ncKey_2":@"ncValue_2"}];
}
添加了兩個name為空的監(jiān)聽者,object一個為當前控制器峻呕,一個為字符串123
發(fā)送通知時name不可以為空
2018-11-22 17:19:32.028685+0800 Nunca[51024:7816220] selector---NSConcreteNotification 0x604000447440 {name = noti_name1; object = <NcNotificationVCtr: 0x7f9bd351bcc0>; userInfo = {
"ncKey_1" = "ncValue_1";
}}
2018-11-22 17:19:32.028979+0800 Nunca[51024:7816220] block---NSConcreteNotification 0x604000447440 {name = noti_name2; object = 123; userInfo = {
"ncKey_2" = "ncValue_2";
}}
監(jiān)聽者name為空利职、object不為空時,只要發(fā)送通知時傳入的object是與監(jiān)聽者的相同瘦癌、不管發(fā)送通知的name是什么通知都能被成功監(jiān)聽猪贪。
1.4 若name與object都不為空,則只有發(fā)送時通知名稱與name相同并且傳入的object與監(jiān)聽傳入的object為同一個對象的通知能被成功監(jiān)聽
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receviedNoti:) name:@"noti_name" object:self];
[[NSNotificationCenter defaultCenter] addObserverForName:@"noti_name" object:@"123" queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"block---%@",note);
}];
}
- (void)receviedNoti:(NSNotification *)noti {
NSLog(@"selector---%@",noti);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_name" object:self userInfo:@{@"ncKey_1":@"ncValue_1"}];
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_name" object:@"123" userInfo:@{@"ncKey_2":@"ncValue_2"}];
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_name" object:nil userInfo:@{@"ncKey_3":@"ncValue_3"}];
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_name_x" object:self userInfo:@{@"ncKey_4":@"ncValue_4"}];
}
若監(jiān)聽者的name與object都不為空讯私,那只有發(fā)送通知時傳入的name與object都與監(jiān)聽者的相同通知才能被成功監(jiān)聽热押。
2 通知發(fā)送與監(jiān)聽的線程
回到以上兩種添加監(jiān)聽者的方法,上面列出來的是它們的一些共性斤寇,那他們的差異也是存在的桶癣。
2.1 queue為nil的時候
對于第一種傳入selector方式,通知在哪個線程發(fā)送則在哪個線程被同步回掉娘锁。
對于第二種傳入block形式的牙寞,可以傳入一個NSOperationQueue,指定block被加入至哪個隊列莫秆,若穿入的是空间雀,則block會被在通知發(fā)送時所在線程同步調(diào)用(與第一種方式相同了)悔详。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"--add observer in thread: %@",[NSThread currentThread]);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receviedNoti:) name:@"noti_name" object:nil];
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"noti_name" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"--block call back in thread: %@",[NSThread currentThread]);
}];
self.notiThread = [[NSThread alloc] initWithTarget:self selector:@selector(notiThreadStart) object:nil];
[self.notiThread start];
}
- (void)receviedNoti:(NSNotification *)noti {
NSLog(@"--selector call back in thread: %@",[NSThread currentThread]);
}
- (void)notiThreadStart {
[[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self performSelector:@selector(notiThreadMethod) onThread:self.notiThread withObject:nil waitUntilDone:NO];
}
- (void)notiThreadMethod {
NSLog(@"--post Noti in thread: %@",[NSThread currentThread]);
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_name" object:self userInfo:nil];
NSLog(@"--finish");
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver: _observer];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
在主線程添加了通知監(jiān)聽者(以第二種方式添加的監(jiān)聽者傳入的queue為nil),然后在子線程發(fā)送通知雷蹂。
另外可以看到第一種方式添加監(jiān)聽者時伟端,監(jiān)聽者是我們自己傳入指定的,第二種方式則是創(chuàng)建并返回了一個監(jiān)聽者匪煌,因此第二種需要我們額外保存返回的observer责蝠,以便于在何時的時機移除。
2018-11-22 18:10:29.882628+0800 Nunca[51489:7869385] --add observer in thread: <NSThread: 0x600000065e40>{number = 1, name = main}
2018-11-22 18:10:35.204503+0800 Nunca[51489:7869593] --post Noti in thread: <NSThread: 0x604000470c00>{number = 3, name = (null)}
2018-11-22 18:10:35.205316+0800 Nunca[51489:7869593] --selector call back in thread: <NSThread: 0x604000470c00>{number = 3, name = (null)}
2018-11-22 18:10:35.205598+0800 Nunca[51489:7869593] --block call back in thread: <NSThread: 0x604000470c00>{number = 3, name = (null)}
2018-11-22 18:10:35.205969+0800 Nunca[51489:7869593] --finish
可以看到萎庭,監(jiān)聽者接收到通知執(zhí)行監(jiān)聽方法時所在的線程與通知發(fā)送所在線程一致霜医,與添加監(jiān)聽者時所在的線程無關(guān)。另外驳规,在通知發(fā)送后先去執(zhí)行了監(jiān)聽者的方法之后再繼續(xù)執(zhí)行當前代碼(打印--finish)肴敛,說明通知的發(fā)送與監(jiān)聽是同步執(zhí)行的。
2.2 指定queue的時候
對于第二種添加監(jiān)聽者的方式吗购,也可以指定執(zhí)行block所在queue医男,當監(jiān)聽到通知后要更新UI的時候就適合以這種方式添加監(jiān)聽者,此時將queue設(shè)置為mainQueue就好捻勉。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"--add observer in thread: %@",[NSThread currentThread]);
[[NSNotificationCenter defaultCenter] addObserverForName:@"noti_name" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"--block call back in thread: %@",[NSThread currentThread]);
for (int i = 0; i < 500; i++) {
NSLog(@"--block--%d",i);
}
}];
self.notiThread = [[NSThread alloc] initWithTarget:self selector:@selector(notiThreadStart) object:nil];
[self.notiThread start];
}
- (void)notiThreadStart {
[[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self performSelector:@selector(notiThreadMethod) onThread:self.notiThread withObject:nil waitUntilDone:NO];
}
- (void)notiThreadMethod {
NSLog(@"--post Noti in thread: %@",[NSThread currentThread]);
[[NSNotificationCenter defaultCenter] postNotificationName:@"noti_name" object:self userInfo:nil];
NSLog(@"--finish");
}
將queue指定為mainQueue
2018-11-22 18:30:39.598772+0800 Nunca[51794:7888240] --add observer in thread: <NSThread: 0x600000071380>{number = 1, name = main}
2018-11-22 18:30:41.612623+0800 Nunca[51794:7888393] --post Noti in thread: <NSThread: 0x60000027cac0>{number = 3, name = (null)}
2018-11-22 18:30:41.613826+0800 Nunca[51794:7888313] --block call back in thread: <NSThread: 0x600000071380>{number = 1, name = main}
2018-11-22 18:30:41.614457+0800 Nunca[51794:7888313] --block--0
2018-11-22 18:30:41.614660+0800 Nunca[51794:7888313] --block--1
2018-11-22 18:30:41.615330+0800 Nunca[51794:7888313] --block--2
2018-11-22 18:30:41.615687+0800 Nunca[51794:7888313] --block--3
...
2018-11-22 18:30:41.827388+0800 Nunca[51794:7888313] --block--497
2018-11-22 18:30:41.827685+0800 Nunca[51794:7888313] --block--498
2018-11-22 18:30:41.828318+0800 Nunca[51794:7888313] --block--499
2018-11-22 18:30:41.828621+0800 Nunca[51794:7888393] --finish
可以看到通知雖然在子線程被發(fā)送镀梭,但監(jiān)聽方法是在主線程執(zhí)行,并且也是同步的踱启,在監(jiān)聽方法中的所有任務(wù)執(zhí)行完成之后才會繼續(xù)原來的程序报账,因此也需要注意,發(fā)送通知時埠偿,很可能因為監(jiān)聽者需要處理耗時任務(wù)而使得通知發(fā)送所在的線程被阻塞透罢。
二、存儲observer的結(jié)構(gòu)
首先根據(jù)GNUstep的代碼冠蒋,找出幾個關(guān)鍵結(jié)構(gòu)
typedef struct NCTbl {
Observation *wildcard; /* Get ALL messages. */
GSIMapTable nameless; /* Get messages for any name. */
GSIMapTable named; /* Getting named messages only. */
unsigned lockCount; /* Count recursive operations. */
NSRecursiveLock *_lock; /* Lock out other threads. */
Observation *freeList;
Observation **chunks;
unsigned numChunks;
GSIMapTable cache[CACHESIZE];
unsigned short chunkIndex;
unsigned short cacheIndex;
} NCTable;
主要關(guān)注前三個:
1.Observation *wildcard;
typedef struct Obs {
id observer; /* Object to receive message. */
SEL selector; /* Method selector. */
struct Obs *next; /* Next item in linked list. */
int retained; /* Retain count for structure. */
struct NCTbl *link; /* Pointer back to chunk table */
} Observation;
以鏈表的方式存儲observers羽圃,里面存放的observer監(jiān)聽所有通知。
2.GSIMapTable nameless
struct _GSIMapTable {
NSZone *zone;
uintptr_t nodeCount; /* Number of used nodes in map. */
uintptr_t bucketCount; /* Number of buckets in map. */
GSIMapBucket buckets; /* Array of buckets. */
GSIMapNode freeNodes; /* List of unused nodes. */
uintptr_t chunkCount; /* Number of chunks in array. */
GSIMapNode *nodeChunks; /* Chunks of allocated memory. */
uintptr_t increment;
#ifdef GSI_MAP_EXTRA
GSI_MAP_EXTRA extra;
#endif
};
struct _GSIMapBucket {
uintptr_t nodeCount; /* Number of nodes in bucket. */
GSIMapNode firstNode; /* The linked list of nodes. */
};
struct _GSIMapNode {
GSIMapNode nextInBucket; /* Linked list of bucket. */
GSIMapKey key;
#if GSI_MAP_HAS_VALUE
GSIMapVal value;
#endif
};
當observer的name為空浊服、object不為空的時候统屈,則會被存入nameless。
以object為key牙躺,以observer的集合為value,構(gòu)成一個MapNode結(jié)構(gòu)腕扶,再以鏈表形式保存在nameless孽拷。
3.GSIMapTable named;
當observer的name不為空時,observer被存入named半抱。
以observer的name為key脓恕,value為一個集合(or鏈表結(jié)構(gòu))膜宋,集合中存儲的結(jié)構(gòu)是以object為key、以對應(yīng)的observers為value炼幔。
添加observer時的具體步驟:
1.判斷observer的name是否為空秋茫,如果不為空則進入named表,再以observer的name為key去named表中索引對應(yīng)的下一層級的表乃秀,如果索引出來不存在則創(chuàng)建肛著,取得下一層級的表的時候,再以observer的object為key去索引跺讯,object即使為空也可以索引到對應(yīng)的集合(或者鏈表)枢贿,將observer存入。
2.如果observer的name為空刀脏,則繼續(xù)判斷object是否為空局荚,若object不為空,則以object為key去nameless表中查找對應(yīng)的集合愈污,并將observer存入
3.如果object也為空耀态,則將observer添加至wildcard
移除observer時的具體步驟:
1.如果name與object都為空
1.1 先去wildcard去查找、刪除
1.2 再繼續(xù)步驟2與3
2.如果name為空
.......(有空再補充)
3.如果name不為空
.......(有空再補充)
三暂雹、發(fā)送通知的方式
1.直接post方式
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
這是最常見的發(fā)送通知的方式首装,上面的例子也全部基于此種通知發(fā)送的方式。這種方式發(fā)送的通知都是同步地去執(zhí)行監(jiān)聽方法擎析。
2.NSNotificationQueue
2.1NSNotificationQueue的結(jié)構(gòu)
@interface NSNotificationQueue : NSObject {
@private
id _notificationCenter;
id _asapQueue;
id _asapObs;
id _idleQueue;
id _idleObs;
}
2.2獲取NSNotificationQueue對象的方式
// 類對象 NSNotificationQueue.defaultQueue;
@property (class, readonly, strong) NSNotificationQueue *defaultQueue;
// 初始化NSNotificationQueue的指定方式
- (instancetype)initWithNotificationCenter:(NSNotificationCenter *)notificationCenter NS_DESIGNATED_INITIALIZER;
在同一線程中通過以上兩種方式獲取到的都是同一個NSNotificationQueue對象簿盅,即使多次調(diào)用initWithNotificationCenter,實際上也都是返回的同一個揍魂,只有切換線程桨醋,獲取的才會是一個不一樣的。
(這點與GNUstep的源碼是有出路的现斋,按照GNUstep中的實現(xiàn)每次initWithNotificationCenter應(yīng)該都會生成一個新的queue對象喜最,但是在Xcode9.2中運行,每次生成的都是相同的)
NSNotificationQueue *queue = [[NSNotificationQueue alloc] initWithNotificationCenter:[NSNotificationCenter defaultCenter]];
NSNotificationQueue *queue1 = [NSNotificationQueue defaultQueue];
NSNotificationQueue *queue3 = [[NSNotificationQueue alloc] initWithNotificationCenter:[NSNotificationCenter defaultCenter]];
NSNotificationQueue *queue4 = [[NSNotificationQueue alloc] initWithNotificationCenter:[NSNotificationCenter defaultCenter]];
2018-11-23 13:15:32.799013+0800 Nunca[56077:8460141] queue = <NSNotificationQueue: 0x60000044db00>
2018-11-23 13:15:32.799013+0800 Nunca[56077:8460141] queue1 = <NSNotificationQueue: 0x60000044db00>
2018-11-23 13:15:32.799673+0800 Nunca[56077:8460141] queue3 = <NSNotificationQueue: 0x60000044db00>
2018-11-23 13:15:32.800445+0800 Nunca[56077:8460141] queue4 = <NSNotificationQueue: 0x60000044db00>
每個線程存在并指存在一個與之對應(yīng)的NSNotificationQueue對象庄蹋。
2.3添加通知的方法
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle;
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask:(NSNotificationCoalescing)coalesceMask forModes:(nullable NSArray<NSRunLoopMode> *)modes;
將通知加入隊列中時瞬内,可以指定postingStyle、coalesceMask限书、modes
postingStyle
postingStyle決定通知發(fā)送的時機虫蝶,有三種:
NSPostWhenIdle:空閑的時候發(fā)送
NSPostASAP:盡快發(fā)送,一般在runloop結(jié)束當前l(fā)oop之后
NSPostNow :立刻發(fā)送倦西,添加通知進隊列地同步發(fā)送 (如果指定了合并策略會先進行通知合并)
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receviedNoti:) name:@"noti_name" object:nil];
}
- (void)receviedNoti:(NSNotification *)noti {
NSLog(@"noti==%@",noti);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSNotificationQueue *queue = [[NSNotificationQueue alloc] initWithNotificationCenter:[NSNotificationCenter defaultCenter]];
NSNotification *noti1 = [[NSNotification alloc] initWithName:@"noti_name" object:nil userInfo:@{@"noti":@"1"}];
NSNotification *noti2 = [[NSNotification alloc] initWithName:@"noti_name" object:@"123" userInfo:@{@"noti":@"2"}];
NSNotification *noti3 = [[NSNotification alloc] initWithName:@"noti_name" object:@"123" userInfo:@{@"noti":@"3"}];
NSNotification *noti4 = [[NSNotification alloc] initWithName:@"noti_name" object:nil userInfo:@{@"noti":@"4"}];
NSLog(@"enqueue noti1");
[queue enqueueNotification:noti1 postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:nil];
NSLog(@"enqueue noti2");
[queue enqueueNotification:noti2 postingStyle:NSPostASAP coalesceMask:NSNotificationNoCoalescing forModes:nil];
NSLog(@"enqueue noti3");
[queue enqueueNotification:noti3 postingStyle:NSPostNow coalesceMask:NSNotificationNoCoalescing forModes:@[@"123"]];
NSLog(@"enqueue noti4");
[queue enqueueNotification:noti4 postingStyle:NSPostNow coalesceMask:NSNotificationNoCoalescing forModes:@[NSRunLoopCommonModes]];
}
2018-11-23 14:32:04.065704+0800 Nunca[57044:8578090] enqueue noti1
2018-11-23 14:32:04.066063+0800 Nunca[57044:8578090] enqueue noti2
2018-11-23 14:32:04.066166+0800 Nunca[57044:8578090] enqueue noti3
2018-11-23 14:32:04.067676+0800 Nunca[57044:8578090] noti==NSConcreteNotification 0x600000448c10 {name = noti_name; object = 123; userInfo = {
noti = 3;
}}
2018-11-23 14:32:04.067813+0800 Nunca[57044:8578090] enqueue noti4
2018-11-23 14:32:04.067985+0800 Nunca[57044:8578090] noti==NSConcreteNotification 0x6000004491e0 {name = noti_name; userInfo = {
noti = 4;
}}
2018-11-23 14:32:04.068216+0800 Nunca[57044:8578090] noti==NSConcreteNotification 0x6000004490c0 {name = noti_name; object = 123; userInfo = {
noti = 2;
}}
2018-11-23 14:32:04.068749+0800 Nunca[57044:8578090] noti==NSConcreteNotification 0x600000448f10 {name = noti_name; userInfo = {
noti = 1;
}}
coalesceMask
coalesceMask提供了合并通知的功能能真,是NS_OPTIONS類型:
NSNotificationNoCoalescing :不合并
NSNotificationCoalescingOnName :將加入新通知之前已經(jīng)存在queue中的name與新通知相同的通知合并(清除)
NSNotificationCoalescingOnSender :將queue中object與當前通知相同的通知合并,只留下當前加入的
除了在添加通知時指定通知的合并策略,也可以調(diào)用dequeueNotificationsMatching: coalesceMask:方法對queue中存在的通知進行清除粉铐。
調(diào)用enqueueNotification:方法時疼约,如果指定了合并策略,底層其實也是在將通知添加進queue前先調(diào)用了一次dequeueNotificationsMatching: coalesceMask:方法蝙泼。
[queue enqueueNotification:noti4 postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil];
===== 這句與下面這兩句代碼 作用是一樣的 =====
[queue dequeueNotificationsMatching:noti4 coalesceMask:NSNotificationCoalescingOnName];
[queue enqueueNotification:noti4 postingStyle:NSPostNow coalesceMask:NSNotificationNoCoalescing forModes:nil];
將前面的代碼修改一下程剥,只在結(jié)尾處添加一個dequeueNotificationsMatching方法:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSNotificationQueue *queue = [[NSNotificationQueue alloc] initWithNotificationCenter:[NSNotificationCenter defaultCenter]];
NSNotification *noti1 = [[NSNotification alloc] initWithName:@"noti_name" object:nil userInfo:@{@"noti":@"1"}];
NSNotification *noti2 = [[NSNotification alloc] initWithName:@"noti_name" object:@"123" userInfo:@{@"noti":@"2"}];
NSNotification *noti3 = [[NSNotification alloc] initWithName:@"noti_name" object:@"123" userInfo:@{@"noti":@"3"}];
NSNotification *noti4 = [[NSNotification alloc] initWithName:@"noti_name" object:nil userInfo:@{@"noti":@"4"}];
NSLog(@"enqueue noti1");
[queue enqueueNotification:noti1 postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:nil];
NSLog(@"enqueue noti2");
[queue enqueueNotification:noti2 postingStyle:NSPostASAP coalesceMask:NSNotificationNoCoalescing forModes:nil];
NSLog(@"enqueue noti3");
[queue enqueueNotification:noti3 postingStyle:NSPostNow coalesceMask:NSNotificationNoCoalescing forModes:@[@"123"]];
NSLog(@"enqueue noti4");
[queue enqueueNotification:noti4 postingStyle:NSPostNow coalesceMask:NSNotificationNoCoalescing forModes:@[NSRunLoopCommonModes]];
[queue dequeueNotificationsMatching:noti2 coalesceMask:NSNotificationCoalescingOnName];
}
2018-11-23 14:35:01.444270+0800 Nunca[57093:8581368] enqueue noti1
2018-11-23 14:35:01.444611+0800 Nunca[57093:8581368] enqueue noti2
2018-11-23 14:35:01.445167+0800 Nunca[57093:8581368] enqueue noti3
2018-11-23 14:35:01.446550+0800 Nunca[57093:8581368] noti==NSConcreteNotification 0x600000241a70 {name = noti_name; object = 123; userInfo = {
noti = 3;
}}
2018-11-23 14:35:01.446687+0800 Nunca[57093:8581368] enqueue noti4
2018-11-23 14:35:01.446859+0800 Nunca[57093:8581368] noti==NSConcreteNotification 0x600000241a40 {name = noti_name; userInfo = {
noti = 4;
}}
在執(zhí)行dequeueNotificationsMatching方法時,queue中存在noti1汤踏、noti2织鲸,由于noti1、noti2與用來匹配的noti2的name一致茎活,因此都被dequeue了昙沦。
modes
modes可以指定通知在runloop的哪幾種mode中執(zhí)行,但是對于postingStyle為NSPostNow的载荔,即使runloop不是運行在指定mode盾饮,通知也會在當前mode立刻被發(fā)送。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSNotificationQueue *queue = [[NSNotificationQueue alloc] initWithNotificationCenter:[NSNotificationCenter defaultCenter]];
NSNotification *noti1 = [[NSNotification alloc] initWithName:@"noti_name" object:nil userInfo:@{@"noti":@"1"}];
NSNotification *noti2 = [[NSNotification alloc] initWithName:@"noti_name" object:@"123" userInfo:@{@"noti":@"2"}];
NSNotification *noti3 = [[NSNotification alloc] initWithName:@"noti_name" object:@"123" userInfo:@{@"noti":@"3"}];
NSNotification *noti4 = [[NSNotification alloc] initWithName:@"noti_name" object:nil userInfo:@{@"noti":@"4"}];
NSLog(@"enqueue noti1");
[queue enqueueNotification:noti1 postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:@[@"123"]];
NSLog(@"enqueue noti2");
[queue enqueueNotification:noti2 postingStyle:NSPostASAP coalesceMask:NSNotificationNoCoalescing forModes:@[NSDefaultRunLoopMode]];
NSLog(@"enqueue noti3");
[queue enqueueNotification:noti3 postingStyle:NSPostNow coalesceMask:NSNotificationNoCoalescing forModes:@[@"123"]];
NSLog(@"enqueue noti4");
[queue enqueueNotification:noti4 postingStyle:NSPostNow coalesceMask:NSNotificationNoCoalescing forModes:@[NSRunLoopCommonModes]];
}
2018-11-23 14:40:49.655868+0800 Nunca[57168:8589299] enqueue noti1
2018-11-23 14:40:49.656529+0800 Nunca[57168:8589299] enqueue noti2
2018-11-23 14:40:49.657081+0800 Nunca[57168:8589299] enqueue noti3
2018-11-23 14:40:49.658542+0800 Nunca[57168:8589299] noti==NSConcreteNotification 0x600000447cb0 {name = noti_name; object = 123; userInfo = {
noti = 3;
}}
2018-11-23 14:40:49.658682+0800 Nunca[57168:8589299] enqueue noti4
2018-11-23 14:40:49.658838+0800 Nunca[57168:8589299] noti==NSConcreteNotification 0x6000004477d0 {name = noti_name; userInfo = {
noti = 4;
}}
2018-11-23 14:40:49.659112+0800 Nunca[57168:8589299] noti==NSConcreteNotification 0x600000447c20 {name = noti_name; object = 123; userInfo = {
noti = 2;
}}
2.4接收通知后監(jiān)聽方法執(zhí)行的線程
對于利用NSNotificationQueue發(fā)送通知的方式懒熙,監(jiān)聽方法執(zhí)行的線程與通知被加入至隊列時所在的線程一致丘损。
四、總結(jié)
使用通知時的注意點:
1.使用 addObserver: selector: 添加監(jiān)聽者時工扎,監(jiān)聽方法執(zhí)行時所在線程與發(fā)送通知所在的線程一致徘钥,若是在監(jiān)聽的方法中添加處理UI或其他需要指定線程處理的任務(wù),則需要注意肢娘。
2.postNotif 方式發(fā)送通知時呈础,會同步去處理監(jiān)聽者的回掉,如果回掉中有耗時任務(wù)橱健,則容易阻塞當前線程而钞。
3.iOS9以后不移除observer也不會導(dǎo)致程序崩潰,但最好當然是要用完就移除啦拘荡。
4........(想到再補充)