GCD源码分析之再解 sentinel

因为作者的疏忽,之前的 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

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

1 收藏 评论

关于作者:酷酷的哀殿

常年混迹于创业公司的 iOS 开发者。 个人主页 · 我的文章 · 11 ·    

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部