前言
上一遍文章介绍了一些iOS系统自带的宏定义如NS_AVAILABLE_IOS(_ios)、UNAVAILABLE_ATTRIBUTE,以及我们平时在开发过程中如何使用这些宏定义来写出更好的代码。但是这个宏定义背后的原理,执行流程却没有详细分析过,经过最近反复的查询资料和调研,现在来说说我理解下的这些宏定义背后的原理。下面就用UNAVAILABLE_ATTRIBUTE来举例。
一.宏定义UNAVAILABLE_ATTRIBUTE背后到底是什么?
点击查看一下系统文档,可以看到下面一段关于UNAVAILABLE_ATTRIBUTE的定义
/*
* only certain compilers support __attribute__((unavailable))
*/
#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)))
#define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))
#else
#define UNAVAILABLE_ATTRIBUTE
#endif
这里说明了UNAVAILABLE_ATTRIBUTE的定义是__attribute__((unavailable)),那么__attribute__((unavailable))又是什么呢?
解答: __attribute__机制 是GNU C 最好的特性之一,它不是标准的C语音,而是GCC中的一个扩展,只供GCC编译器使用的向源代码中注入语法的一种方式。它不是函数调用(虽然看起来挺像),我们可以把它看成是向编译器提供关于它附加到的元素的附加信息,以允许编译器执行更多错误检查。作为iOS开发者可能对这个相对陌生,但是一些学习linux内核代码及一些开源软件的源码的程序员一定会经常看到 __attribute__ 的相关使用。
二. __attribute__怎么用?
1.关于 __attribute__ 书写格式
注意!__attribute__ 前后均有两个下划线,并且后面会紧跟2组小括号,括号里面是相关参数,如:
_attribute__((xxx))
举个例子,每一个oc项目肯定有一个main函数,假如我要在main里使用argv这个参数,而不使用argc这个参数,那么可以写成下面这样:
int main(int argc __attribute__((unused)) , char **argv)
{
/* 使用 argv 但不使用 argc 的代码 */
}
2.关于 __attribute__ 有哪些参数
__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute),其各个属性的具体参数分别如下:
| 函数属性(Function Attribute) | 变量属性(Variable Attribute) | 类型属性(Type Attribute) | Clang特有的 |
|---|---|---|---|
| noreturn | aligned | aligned | availability |
| noinline | packed | packed | overloadable |
| always_inline | transparent_union | ||
| pure | unused | ||
| const | deprecated | ||
| nothrow | may_alias | ||
| sentinel | |||
| format | |||
| format_arg | |||
| no_instrument_function | |||
| section | |||
| constructor | |||
| destructor | |||
| used | |||
| unused | |||
| deprecated | |||
| weak | |||
| malloc | |||
| alias | |||
| warn_unused_result | |||
| nonnull |
例如上面举例了表格里unused的使用,表示参数argc并没有用上,类似objc代码里的nullable。虽然这样写看起来很繁琐,但这些的写法既能让编译器消除警告,也能告诉其他开发者这是故意这么设计的。当然删除实际不使用的变量会更好,但是某些场景下的确会有这种情况发生。
3.关于availability属性
看到这里,可能有些人有点疑惑,我第一点提到了UNAVAILABLE_ATTRIBUTE的定义是__attribute__((unavailable)),为啥这个表格里找不到unavailable呢?其实这是属于Clang特有的availability属性。
availability属性是一个后面紧跟小括号,括号里面以逗号为分隔的参数列表,第一个参数必须声明平台,目前支持的平台是ios、macosx和watchos,有如下几个参数
| availability参数 | 作用 |
|---|---|
| introduced | 声明第一次出现的版本 |
| deprecated | 声明要废弃的版本,可再用,但在编译时会报过时警告 |
| obsoleted | 声明要移除的版本,不可再用 |
| unavailable | 声明函数不可用 |
| message | 可以说明一些关于废弃和移除的额外信息,编译时报警告会提供这些信息 |
而将这些参数与availability结合起来,就是一个详细的特性声明,因此availability的声明可以说是程碑式的。代码举个例子:
- (void)method __attribute__((availability(ios,introduced=3_0,deprecated=7_0,message="只推荐iOS系统3-7使用")));
除此之外,苹果官方也有自己定义的一些宏,方便开发者直接使用,展开后也是和上面的代码一样的,如:
- (void)method1 NS_DEPRECATED_IOS(3_0, 7_0,"只推荐iOS系统3-7使用");
- (void)method2 CF_DEPRECATED_IOS(3_0, 7_0,"只推荐iOS系统3-7使用");
其他更多常用的系统宏定义以及我们应该怎么去使用可以移步我上一篇的文章:总结一下iOS系统的宏定义
三. 关于 __attribute__与非 GNU 编译器的兼容性
因为 __attribute__ 是 GNU C 特有的,如果在别的平台,是需要屏蔽的,也由于 __attribute__ 机制设计巧妙,如果在 GNU C 以外的平台上使用,可以通过判断是否GNU机器来决定是否引入 __attribute__ ,例如:
#if defined(__GNUC__)
#define __attribute__((xxx))
#else
/*other*/
#endif
总结
从宏定义入手,了解底层开发原理,可以有助我们做SDK封装时有更高效的方式,或者提供更简易的使用方法。希望这篇文章对大家有帮助,如有错误望指正。
相关参考链接: