因為作者的疏忽,之前的 GCD原始碼分析之base.h 一文中,遺漏了一個知識點。今天特地更新了原文並單發一篇文章講解,希望能夠和更多的開發者一起分享知識。
sentinel
sentinel
的中文翻譯是哨兵。
經常接觸底層庫開發的程式設計師可能會經常遇到它,在計算機的世界中,它是一個表示程式開始或結束的符號(常見的值為 NULL
)。
iOS 開發者可以通過在 objc4-NSObject.mm 頁面中搜尋
POOL_SENTINEL
來檢視其用法。POOL_SENTINEL
的作用是用來當做不同autorelease pool
的邊界。
當 attribute 和 sentinel 放到一起時,它可以起到通知編譯器:當接收可變引數時,NULL
所在的位置。
下面,我們通過幾個具體的例子來檢視它的用法。
上面的例子中,所有的函式引數都分為兩部分:指標 + 可變引數列表。
__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
個。
修飾符指定了從後往前數第三個為 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
,所以會產生可變引數不夠的警告️。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式