__attribute__(编译属性)知识总结

个人博客地址:iOS开发常用 __attribute__(编译属性)知识总结 - 王小熊他爸的博客

第0部分:说在前面的话

       使用Objective-C作为主要开发语言已经有一段时间了,其独特的语法相信也令初次接触她的人印象颇深。然而语法之外,当我们查看苹果提供的头文件,我还被另外一些符号所吸引,比如 “NS_DESIGNATED_INITIALIZER”、“API_AVAILABLE”等等。这些就是传说中的__attribute__(编译属性)。虽然从字面上很好理解其含义,也能猜到他们是一些与编译器相关的指令,但直到最近,我才终于搞清其背后的原理与历史。


这篇文章,就简单总结下 __attribute__的背后的一些编译器相关的知识与常见编译属性用法。

首先,引用一句比较正式的定义:

__attribute__is a compiler directive that specifies characteristics on declarations, which allows for more error checking and advanced optimizations.

实际上,__attribute__是GNU C 的特点之一,在这篇GNU的官方文档中有比较详细的介绍。

简单总结下:_attribute__有多种类型,Function-AttributesVariable-Attributes、Type-Attributes 、Label Attributes和 Enumerator Attributes。

书写特征是:__attribute__前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的__attribute__参数。

语法格式为:__attribute__ ((attribute-list))

位置约束为:放于声明的尾部“;”之前。

而iOS目前的编译器llvm/Clang ,不仅对__attribute__提供了非常良好的支持,同时还增加了一些扩展,某些attribute甚至在iOS开发中起到了不可或缺的作用,下面就总结下在iOS开发中,比较常见和有趣的一些__attribute__用法:

第一部分:开发中比较常见的:

1、deprecated

在iOS中,常见的NS_DEPRECATED()宏实际就是__attribute__((deprecated)),还可以增加一些提示语变为__attribute__((deprecated(s)))。

2、availability

3、overloadable

Clang provides support for C++ function overloading in C. Function overloading in C is introduced using theoverloadableattribute. For example, one might provide several overloaded versions of atgsinfunction that invokes the appropriate standard function computing thesineof a value withfloat,double, orlong doubleprecision:

#include float__attribute__((overloadable))tgsin(floatx){returnsinf(x);}double__attribute__((overloadable))tgsin(doublex){returnsin(x);}longdouble__attribute__((overloadable))tgsin(longdoublex){returnsinl(x);}

4、format (gcc)

Theformatattribute specifies that a function takesprintf,scanf,strftimeorstrfmonstyle arguments which should be type-checked against a format string.

比如:

externintmy_printf(void*my_object,constchar*my_format,...)__attribute__((format(printf,2,3)));

5、nonnull (gcc)

extern void*my_memcpy(void*dest,constvoid*src,size_tlen)__attribute__((nonnull(1,2)));

iOS中对于nonnull使用不多,更为常见的是苹果在Xcode 6.3引入的一个Objective-C的新特性:nullability annotations

6、unused

This attribute, attached to a function, means that the function is meant to be possibly unused. GCC will not produce a warning for this function.

简而言之,可以用它来避免编译器产生unused警告。

7、unavailable 

用法:__attribute__((unavailable)),NS_UNAVAILABLE

这个挺实用,它的作用是告诉编译器该方法不可用,如果强行调用编译器会提示错误。比如某个类在构造的时候不想直接通过init来初始化,只能通过特定的初始化方法,就可以将init方法标记为unavailable。

其他常见的还有很多,不一一列举了,详见https://clang.llvm.org/docs/AttributeReference.html

第二部分:开发中不常使用,但恰当使用能起到事半功倍的效果:

1、__attribute__((objc_requires_super))

aka: NS_REQUIRES_SUPER,标志子类继承这个方法时需要调用 super,否则给出编译警告。

2、objc_subclassing_restricted

用来定义一个 Final Class

3、constructor / destructor

(该部分引用自https://blog.sunnyxx.com/2016/05/14/clang-attributes/)顾名思义,构造器和析构器,加上这两个属性的函数会在分别在可执行文件(或 shared library)load和 unload 时被调用,可以理解为在 main() 函数调用前和 return 后执行:

__attribute__((constructor))

static void beforeMain(void) {

NSLog(@"beforeMain");

}

__attribute__((destructor))

static void afterMain(void) {

NSLog(@"afterMain");

}

int main(int argc, const char * argv[]) {

NSLog(@"main");

return 0;

}

// Console:

// "beforeMain" -> "main" -> "afterMain"

constructor 和 +load 都是在 main 函数执行前调用,但 +load 比 constructor 更加早一些,因为 dyld(动态链接器,程序的最初起点)在加载 image(可以理解成 Mach-O 文件)时会先通知 objc runtime 去加载其中所有的类,每加载一个类时,它的 +load 随之调用,全部加载完成后,dyld 才会调用这个 image 中所有的 constructor 方法。

所以 constructor 是一个干坏事的绝佳时机:

所有 Class 都已经加载完成

main 函数还未执行

无需像 +load 还得挂载在一个 Class 中

PS:若有多个 constructor 且想控制优先级的话,可以写成 __attribute__((constructor(101))),里面的数字越小优先级越高,1 ~ 100 为系统保留。

4、objc_runtime_name

这个属性就非常有意思了,用于 @interface 或 @protocol,将类或协议的名字在编译时指定成另一个。

之前在项目中经常有一些需求需要修改类名,通过这个__attribute__只需一行代码就可以直接在完成了,awesome!但需要注意的是如果代码中有用反射就会出问题。或者做一些简单的类名混淆:

__attribute__((objc_runtime_name("abcdefghighlmn")))

@interface SecretClass : NSObject

@end

5、cleanup

作用:离开作用域之后执行指定的方法。实际应用中可以在作用域结束之后做一些特定的工作,比如清理。

Reactive Cocoa 用这个特性实现了神奇的 @onExit,关于这个 attribute,在sunnyxx的这篇博客中有非常详细的介绍。

6、objc_designated_initializer (NS_DESIGNATED_INITIALIZER)

aka:NS_DESIGNATED_INITIALIZER,这也是一个在iOS中非常常见的attribute,它可以指定类的初始化方法。指定初识化方法并不是对使用者。而是对内部的现实。

具体的讲解可以参见这篇文章:https://segmentfault.com/a/1190000004116196

这个attribute的目的其实是在初始化类的实例时,无论调用关系如何复杂,必须调用一次该类的Designated intializer(可以有多个),对于 Designated intializer,必须调用父类的Designated intializer。对于父类的父类这个规则亦然,对Designated intializer的调用一直要到根类。

7、objc_boxable

OC中常见的 @(10)这样的写法,它的实现方式就是利用该Function attributes:

struct __attribute__((objc_boxable)) some_struct {

int i;

};

union __attribute__((objc_boxable)) some_union {

int i;

float f;

};

typedef struct __attribute__((objc_boxable)) _some_struct some_struct;

some_struct ss;

NSValue *boxed = @(ss);


8、一些与内存管理相关的__attribute__

__attribute__((objc_precise_lifetime))

用这个attribute修饰一个变量,防止ARC过早释放它。即向编译器表明,变量在它所处的整个作用域内,都应被认为是有用的。

Foundation里提供了宏NS_VALID_UNTIL_END_OF_SCOPE。

__attribute__((objc_returns_inner_pointer))

用于修饰一个方法,防止ARC过早释放这个方法的所有者。Foundation里提供了宏NS_RETURNS_INNER_POINTER。

__attribute__((ns_returns_retained))

在Objective-C里,很多方法的命名都是有约定的含义的。比如以alloc、new、copy、mutableCopy开头的方法,会返回一个retain count为1的对象。对于那些未遵守这些约定,却做了类似操作的方法,可以使用__attribute__((ns_returns_retained))来标明,但如果方法遵守了命名约定,就不必再修饰。

还有很多,不一一赘述,这篇文章中有较为细致的讲解:(传送门)。



结语:

尽管本文列举了很多attribute,但它们仍然只是GCC/Clang中有关attribute的一小部分。

尽管在代码中我们可以从不使用attribute,但了解和使用attribute仍然会带给我们巨大的收益:

这不仅仅体现在通过合理使用attribute,做到更好的编译器优化、编译器检查、内存管理、自动代码生成等;

更重要的是通过这些attribute,能够使得代码的可读性、可维护性得到大大提高,让后继者能拥有一份赏心悦目的代码未尝不是一件值得高兴的事呢。


参考资料:

https://nshipster.com/__attribute__/

http://www.unixwiz.net/techtips/gnu-c-attributes.html

https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

https://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Type-Attributes.html

https://clang.llvm.org/docs/AttributeReference.html

https://blog.sunnyxx.com/2016/05/14/clang-attributes/

http://blog.sunnyxx.com/2014/09/15/objc-attribute-cleanup/

https://segmentfault.com/a/1190000003887934

https://medium.com/@liushuaikobe/%E4%B8%8Eclang%E5%85%B1%E8%88%9E-attribute-8b6bc839958c

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容