
image.png
通知
一、 通知實(shí)現(xiàn)原理是什么?
1、首先從結(jié)構(gòu)、如何存、通知名、事件幾個(gè)方面理解
結(jié)構(gòu):
NSNotification 通知的模型 name、object、userinfo.
NSNotificationCenter通知中心 負(fù)責(zé)發(fā)送NSNotification
NSNotificationQueue通知隊(duì)列 負(fù)責(zé)在某些時(shí)機(jī)觸發(fā) 調(diào)用NSNotificationCenter通知中心 post通知
- 通知是結(jié)構(gòu)體通過雙向鏈表進(jìn)行數(shù)據(jù)存儲(chǔ)
// 根容器,NSNotificationCenter持有
typedef struct NCTbl {
Observation *wildcard; /* 鏈表結(jié)構(gòu),保存既沒有name也沒有object的通知 */
GSIMapTable nameless; /* 存儲(chǔ)沒有name但是有object的通知 */
GSIMapTable named; /* 存儲(chǔ)帶有name的通知,不管有沒有object */
...
} NCTable;
// Observation 存儲(chǔ)觀察者和響應(yīng)結(jié)構(gòu)體,基本的存儲(chǔ)單元
typedef struct Obs {
id observer; /* 觀察者,接收通知的對象 */
SEL selector; /* 響應(yīng)方法 */
struct Obs *next; /* Next item in linked list. */
...
} Observation;
- 主要是以key value的形式存儲(chǔ),這里需要重點(diǎn)強(qiáng)調(diào)一下 通知以 name和object兩個(gè)緯度來存儲(chǔ)相關(guān)通知內(nèi)容,也就是我們添加通知的時(shí)候傳入的兩個(gè)不同的方法.
通知的發(fā)送時(shí)同步的,還是異步的?
- 同步發(fā)送.因?yàn)橐{(diào)用消息轉(zhuǎn)發(fā).所謂異步,指的是非實(shí)時(shí)發(fā)送而是在合適的時(shí)機(jī)發(fā)送,并沒有開啟異步線程.
NSNotificationCenter接受消息和發(fā)送消息是在一個(gè)線程里嗎?如何異步發(fā)送消息?
- 是的, 異步線程發(fā)送通知?jiǎng)t響應(yīng)函數(shù)也是在異步線程。異步發(fā)送通知可以開啟異步線程發(fā)送即可.
NSNotificationQueue是異步還是同步發(fā)送?在哪個(gè)線程響應(yīng)?
- NSNotificationCenter都是同步發(fā)送的,而這里介紹關(guān)于NSNotificationQueue的異步發(fā)送,從線程的角度看并不是真正的異步發(fā)送,或可稱為延時(shí)發(fā)送,它是利用了runloop的時(shí)機(jī)來觸發(fā)的.
- 異步線程發(fā)送通知?jiǎng)t響應(yīng)函數(shù)也是在異步線程,主線程發(fā)送則在主線程.
NSNotificationQueue和runloop的關(guān)系
NSNotificationQueue依賴runloop. 因?yàn)橥ㄖ?duì)列要在runloop回調(diào)的某個(gè)時(shí)機(jī)調(diào)用通知中心發(fā)送通知.從下面的枚舉值就能看出來
// 表示通知的發(fā)送時(shí)機(jī)
typedef NS_ENUM(NSUInteger, NSPostingStyle) {
NSPostWhenIdle = 1, // runloop空閑時(shí)發(fā)送通知
NSPostASAP = 2, // 盡快發(fā)送,這種時(shí)機(jī)是穿插在每次事件完成期間來做的
NSPostNow = 3 // 立刻發(fā)送或者合并通知完成之后發(fā)送
};
## 如何保證通知接收的線程在主線程
1、如果想在主線程響應(yīng)異步通知的話可以用如下兩種方式
1、在這個(gè)系統(tǒng)方法中指定隊(duì)列
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block
2.NSMachPort的方式 通過在主線程的runloop中添加machPort,設(shè)置這個(gè)port的delegate,通過這個(gè)Port其他線程可以跟主線程通信,在這個(gè)port的代理回調(diào)中執(zhí)行的代碼肯定在主線程中運(yùn)行,所以,在這里調(diào)用NSNotificationCenter發(fā)送通知即可
頁面銷毀時(shí)不移除通知會(huì)崩潰嗎?
- 會(huì)的
iOS9.0之前,會(huì)crash,原因:通知中心對觀察者的引用是unsafe_unretained,導(dǎo)致當(dāng)觀察者釋放的時(shí)候,觀察者的指針值并不為nil,出現(xiàn)野指針.
iOS9.0之后,不會(huì)crash,原因:通知中心對觀察者的引用是weak。
多次添加同一個(gè)通知會(huì)是什么結(jié)果?多次移除通知呢
多次添加同一個(gè)通知,會(huì)導(dǎo)致發(fā)送一次這個(gè)通知的時(shí)候,響應(yīng)多次通知回調(diào)。 多次移除通知不會(huì)產(chǎn)生crash。