GCD源码分析之base.h

480 查看

本文属于原创文章。转载请联系作者。1711809-d203d3a59d024556

GCD & Base

前言

GCD 是一个跨平台的库,它使用了大量的系统底层知识以及编译器优化内容。
本系列文章将主要分析该库的一些值得学习的地方。取其精华,去其糟粕。

base

base.h 主要声明了库中常用的宏。通过它,GCD 实现了对多平台的兼容。

attribute

__attribute__GCC 的一大特色1clang 实现大部分的描述符并添加了一些扩展2

通过它,开发者可以告诉编译器更多的信息以达到优化程序的目的。本文的主要内容即为分析 base.h 文件内的 __attribute__

noreturn

#define DISPATCH_NORETURN __attribute__((__noreturn__)) 宏将 __attribute__((__noreturn__)) 定义为 DISPATCH_NORETURN

noreturn 用于告诉编译器,该函数没有返回(既不需要 return ... 语句)。

noreturn 有如下实际作用:

  • 优化代码函数的调用者在调用函数返回后可能需要做一些清理工作,比如调整桟指针。
    当一个函数被标记为 __noreturn__ 时,那么编译器就可以知道这些清理工作能够被省略掉,这样可以达到少生成一些无用的代码的目的。
  • 抑制编译器的某些警告在公共头文件中,只有 DISPATCH_EXPORT DISPATCH_NOTHROW DISPATCH_NORETURN void dispatch_main(void); 方法使用了该宏。

    因为,我们在正常的应用开发中不会使用到该方法。所以,下面通过其它的示例代码对其进行说明。

    void objc_terminate(void)objc 库的一个退出方法。

    在这里,通过 extern 将其重新声明并添加 DISPATCH_NORETURN 宏。

    int sun_terminater() 是我自定义的一个终止函数,它不接收参数,并返回 int 类型的结果。

    在 clang 中,当函数声明了返回值,却没有提供时,编译器报出Control reaches end of non-void function的❗️错误提示。如下图所示。

    111711809-2c7a073f8d455c31

     但是,这里因为有被 noreturn 描述的函数 objc_terminate(); 的存在,原来的错误提示会被编译器丢失,即开发者看不到原先的❗️错误提示。

    一种更复杂的情况是,当函数有分支处理时,需要所有的分支都包含有被noreturn 描述的任意函数。否则,系统仍然会提示错误。如下图所示。

    121711809-38f2816a9df1015d

nothrow

#define DISPATCH_NOTHROW __attribute__((__nothrow__)) 宏将 __attribute__((__nothrow__)) 定义为 DISPATCH_NOTHROW

该描述符的含义是,函数不会抛出异常。因为 iOS 中使用异常处理的情况比较少,所以这里就不进行展开讲解。

nonnull

__nonnull__用于指定函数或方法的入参类型不为空指针。

它有两种写法,__attribute__((__nonnull__)) 或者 __attribute__((__nonnull__(1,2))

第一种要求所有的入参类型不为空指针,第二种要求指定位置的参数不为空指针。

比如下面的方法声明中,要求第二个参数 block 不能为空指针。当 block 为空时,根本没有必要调用该方法,所以这里使用 DISPATCH_NONNULL2 对其进行限制。

sentinel

#define DISPATCH_SENTINEL __attribute__((__sentinel__)) 宏将 __attribute__((__sentinel__)) 定义为 DISPATCH_SENTINEL

__sentinel__ 的中文意思是哨兵。

当接收可变参数时,可以通过它指定NULL所在的位置。

默认情况下,NULL所在的位置为从后往前数第0个为NULL。
常见于数组或者字典等集合类型接受多个参数时指定最后一个为NULL。
通过它,函数内部可以知道何时终止参数的处理。

示例如下代码如下:

131711809-c33a8382da3168f9

修饰符指定了从后往前数第三个为NULL,如果不满足这个规则,编译器会产生警告⚠️。

pure

#define DISPATCH_PURE __attribute__((__pure__)) 宏将 __attribute__((__pure__)) 定义为 DISPATCH_PURE

__pure__ 的中文意思是纯净的。它只依赖于入参和全局变量进行处理。并且不会对外部造成影响。

pure 修饰的函数不具有可重入性,但是可以通过关中断、信号量(即P、V操作)等手段对全局变量加以保护以实现。

当被 pure 修饰的函数的返回结果没有被使用时,编译器会直接丢弃调用被 pure 修饰的函数的相关代码。

下面以 Implications of pure and constant functions中的例子进行说明。

代码分析:

我们很容易发现,testfunction 函数内的部分代码是可以被忽略的。

比如 int res2 = somepurefunction(b, c);,只有返回值 res2 是由该函数生成的,在它的内部不会影响全局变量,所以该行代码可以全部忽略。

代码行:int res2 = somepurefunction(b, c); 在执行过程中可能影响全局变量,所以无法忽略,但是其返回值没有用处,所以,返回值可以忽略返回值。

代码行: int res3 = a + b - c; 很明显,因为返回结果 res3 没有使用,该行代码可以忽略掉。

所以,编译优化后代码为:

const

DISPATCH_CONST __attribute__((__const__)) 宏将 __attribute__((__const__)) 定义为 DISPATCH_CONST

__const____pure__ 类似,但是 __const 不能依赖于全局变量。也就意味着,const 修饰的函数是幂等的,具有可重入性。

下面仍然通过改造 Implications of pure and constant functions 中的例子进行说明。