GCD原始碼分析之再解 sentinel

酷酷的哀殿發表於2016-09-25

因為作者的疏忽,之前的 GCD原始碼分析之base.h 一文中,遺漏了一個知識點。今天特地更新了原文並單發一篇文章講解,希望能夠和更多的開發者一起分享知識。

sentinel

sentinel 的中文翻譯是哨兵。

經常接觸底層庫開發的程式設計師可能會經常遇到它,在計算機的世界中,它是一個表示程式開始或結束的符號(常見的值為 NULL)。

iOS 開發者可以通過在 objc4-NSObject.mm 頁面中搜尋 POOL_SENTINEL 來檢視其用法。POOL_SENTINEL 的作用是用來當做不同 autorelease pool 的邊界。

當 attribute 和 sentinel 放到一起時,它可以起到通知編譯器:當接收可變引數時,NULL 所在的位置。

下面,我們通過幾個具體的例子來檢視它的用法。

111711809-d48d5ec299171f5f

上面的例子中,所有的函式引數都分為兩部分:指標 + 可變引數列表

__attribute__ ((sentinel)) __attribute__ ((sentinel(0))) __attribute__ ((sentinel(0, 0))) 三種寫法的作用是一樣的,它們表示可變引數中,從後朝前數,第0個引數為 NULL

__attribute__ ((sentinel(2)))__attribute__ ((sentinel(2, 0))) 的作用是一樣的,它們表示可變引數中,從後朝前數,第2個引數為 NULL

小結1:__attribute__ ((sentinel(n))) 限制了可變引數的引數數量至少為 n+1

細心的讀者已經注意到,最後一個函式的描述符的寫法是 __attribute__ ((sentinel(2, 1)));。它和上一個描述符的區別是由 0 變為了 1。呼叫該函式時,可變引數列表部分沒有 NULL

實際上,在 sentinel 的相關用法中,開發者可以通過設定第二個引數為 1 的方式告訴編譯器,可變引數列表前面的引數也當做可變引數列表的一部分。這也意味著,當前面的引數為 NULL 時,即使引數個數為 2,沒有滿足 n+1,也是合法的行為。

小結2:__attribute__ ((sentinel(n,1))) 意味著可變引數的引數數量在前一個引數為 NULL 的情況下,可以為 n 個。

示例如下程式碼如下:
121711809-c33a8382da3168f9

修飾符指定了從後往前數第三個為 NULL,如果不滿足這個規則,編譯器會產生警告️。

sentinel(n, 0) 和 sentinel(n, 1)

sentinel(n, 0) 和 sentinel(n, 1) 之間的區別是處理變長引數前面最後一個被命名引數的方式不同。比如,下面示例中的 void* a。sentinel(0, 0) 意味著該引數 不被包含到空值中斷列表中。sentinel(0,1) 意味著該引數被包含到空值中斷列表中。

在下面的示例中,第二個函式使用 sentinel(0, 1) 進行修飾,用容易理解的讀法就是,可變列表中,最後一個 NULL 後面有 0 個變數,該 NULL 值可以出現在 void *a 的位置也可以

而第一個函式使用 sentinel(0, 0) 進行修飾,用容易理解的讀法就是, void *a 後面的可變列表中,最後一個 NULL 後面有 0 個變數。因為呼叫函式時,只傳了一個 NULL,引數,該引數只滿足了對 void *a 的賦值,卻不滿足從後往前數第0個為 NULL,所以會產生可變引數不夠的警告️。

131711809-5ae6a9abae5aa53c

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

GCD原始碼分析之再解 sentinel GCD原始碼分析之再解 sentinel

相關文章