2020 最新iOS面試題之NSNotification(附答案) 總結 推薦

主要內容包含如下:

  • 實現原理(結構設計、通知如何存儲的、name&observer&SEL之間的關係等)
  • 通知的發送時同步的,還是異步的
  • NSNotificationCenter接受消息和發送消息是在一個線程裏嗎?如何異步發送消息
  • NSNotificationQueue是異步還是同步發送?在哪個線程響應
  • NSNotificationQueue和runloop的關係
  • 如何保證通知接收的線程在主線程
  • 頁面銷燬時不移除通知會崩潰嗎
  • 多次添加同一個通知會是什麼結果?多次移除通知呢
  • 下面的方式能接收到通知嗎?爲什麼
// 發送通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"TestNotification" object:@1];
// 接收通知
[NSNotificationCenter.defaultCenter postNotificationName:@"TestNotification" object:nil];

在解釋這些內容之前 強烈建議認真研讀一下這篇 一文全解iOS通知機制(經典收藏)文章 瞭解一下大概 所有的問題就迎刃而解了.

實現原理(結構設計、通知如何存儲的、name&observer&SEL之間的關係等

首先通知中心結構大概分爲如下幾個類

  • NSNotification 通知的模型 name、object、userinfo.
  • NSNotificationCenter通知中心 負責發送NSNotification
  • NSNotificationQueue通知隊列 負責在某些時機觸發 調用NSNotificationCenter通知中心 post通知

通知是結構體通過雙向鏈表進行數據存儲

// 根容器,NSNotificationCenter持有
typedef struct NCTbl {
  Observation       *wildcard;  /* 鏈表結構,保存既沒有name也沒有object的通知 */
  GSIMapTable       nameless;   /* 存儲沒有name但是有object的通知 */
  GSIMapTable       named;      /* 存儲帶有name的通知,不管有沒有object  */
    ...
} NCTable;

// Observation 存儲觀察者和響應結構體,基本的存儲單元
typedef struct  Obs {
  id        observer;   /* 觀察者,接收通知的對象  */
  SEL       selector;   /* 響應方法     */
  struct Obs    *next;      /* Next item in linked list.    */
  ...
} Observation;

主要是以key value的形式存儲,這裏需要重點強調一下 通知以 nameobject兩個緯度來存儲相關通知內容,也就是我們添加通知的時候傳入的兩個不同的方法.


簡單理解name&observer&SEL之間的關係就是name作爲key, observer作爲觀察者對象,當合適時機觸發就會調用observerSEL.這基本很簡單,如果覺得我說的不準確可以看下文章開頭的文章.

通知的發送時同步的,還是異步的

同步發送.因爲要調用消息轉發.所謂異步,指的是非實時發送而是在合適的時機發送,並沒有開啓異步線程.

NSNotificationCenter接受消息和發送消息是在一個線程裏嗎?如何異步發送消息

是的, 異步線程發送通知則響應函數也是在異步線程.

異步發送通知可以開啓異步線程發送即可.

NSNotificationQueue是異步還是同步發送?在哪個線程響應

// 表示通知的發送時機
typedef NS_ENUM(NSUInteger, NSPostingStyle) {
    NSPostWhenIdle = 1, // runloop空閒時發送通知
    NSPostASAP = 2, // 儘快發送,這種時機是穿插在每次事件完成期間來做的
    NSPostNow = 3 // 立刻發送或者合併通知完成之後發送
};
NSPostWhenIdle NSPostASAP NSPostNow
NSPostingStyle 異步發送 異步發送 同步發送

NSNotificationCenter都是同步發送的,而這裏介紹關於NSNotificationQueue的異步發送,從線程的角度看並不是真正的異步發送,或可稱爲延時發送,它是利用了runloop的時機來觸發的.

異步線程發送通知則響應函數也是在異步線程,主線程發送則在主線程.

NSNotificationQueue和runloop的關係

NSNotificationQueue依賴runloop. 因爲通知隊列要在runloop回調的某個時機調用通知中心發送通知.從下面的枚舉值就能看出來

// 表示通知的發送時機
typedef NS_ENUM(NSUInteger, NSPostingStyle) {
    NSPostWhenIdle = 1, // runloop空閒時發送通知
    NSPostASAP = 2, // 儘快發送,這種時機是穿插在每次事件完成期間來做的
    NSPostNow = 3 // 立刻發送或者合併通知完成之後發送
};

如何保證通知接收的線程在主線程

如果想在主線程響應異步通知的話可以用如下兩種方式

1.系統接受通知的API指定隊列

- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block

2.NSMachPort的方式 通過在主線程的runloop中添加machPort,設置這個port的delegate,通過這個Port其他線程可以跟主線程通信,在這個port的代理回調中執行的代碼肯定在主線程中運行,所以,在這裏調用NSNotificationCenter發送通知即可

頁面銷燬時不移除通知會崩潰嗎?

iOS9.0之前,會crash,原因:通知中心對觀察者的引用是unsafe_unretained,導致當觀察者釋放的時候,觀察者的指針值並不爲nil,出現野指針.

iOS9.0之後,不會crash,原因:通知中心對觀察者的引用是weak。

多次添加同一個通知會是什麼結果?多次移除通知呢

多次添加同一個通知,會導致發送一次這個通知的時候,響應多次通知回調。 多次移除通知不會產生crash。

下面的方式能接收到通知嗎?爲什麼

// 發送通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"TestNotification" object:@1];
// 接收通知
[NSNotificationCenter.defaultCenter postNotificationName:@"TestNotification" object:nil];

不能

首先我們看下通知中心存儲通知觀察者的結構

// 根容器,NSNotificationCenter持有
typedef struct NCTbl {
  Observation  *wildcard;    /* 鏈表結構,保存既沒有name也沒有object的通知 */
  GSIMapTable nameless;    /* 存儲沒有name但是有object的通知    */
  GSIMapTable named;        /* 存儲帶有name的通知,不管有沒有object    */
    ...
} NCTable;

// Observation 存儲觀察者和響應結構體,基本的存儲單元
typedef struct Obs {
  id observer;    /* 觀察者,接收通知的對象    */
  SEL selector;    /* 響應方法        */
  struct Obs *next;        /* Next item in linked list.    */
  ...
} Observation;

namelessnamed的具體數據結構如下:


當添加通知監聽的時候,我們傳入了nameobject,所以,觀察者的存儲鏈表是這樣的:

named表:key(name) : value->key(object) : value(Observation)

因此在發送通知的時候,如果只傳入name而並沒有傳入object,是找不到Observation的,也就不能執行觀察者回調.

總結

今天又重新認識了iOS中的通知中心,希望大家經常溫故而知新.

推薦

收錄:原文地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章