聊聊__attribute__关键字
大家好,我是脑洞
TF
,目前就职于阿里巴巴。从事
iOS
开发多年。这是我刚开的公众号。未来我会持续不定期更新,欢迎大家关注我。一起学习,一起探讨,一起成长。
最近一段时间比较忙,在研究内存相关的东西,后续也会给大家分享下。距离上次更新已经一个月了,不知道写什么,在我上一篇文章中的一个细节,让我想起了 一个 知识点。下面就展开说说。有理解错误的地方欢迎私信指正 🙏 。
下面进入正题~~~
这里有一个
__attribute__
((
section
(
"__DATA
,
__cfstring
”)))
。
其中关键词“
__attribute__
”作为一个
iOS
开发者的我们,在系统源码中经常看到。那这个关键词是干什么的呢?
二、 __attribute__ 简介
__attribute__ 是一个用于在声明时指定一些特性的编译器指令,可以做一些错误检查和高级优化工作。
有兴趣的可以看: https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes
三、简单介绍几个 __attribute__ 参数 1 、 __attribute__ ( noreturn ):
这个属性告诉编译器函数不会返回,这可以用来抑制关于未达到代码路径的错
误。
(比如有些方法是不会return的,那这个方法下面的逻辑也不会触达。
如果在noreturn标记过的方法下面添加逻辑,编译器会有提示。
)
2 、 __attribute__ (( visibility ( "hidden ”))): 这个属性 可以隐藏一些外部不需要的符号。这个对包大小都有一定程度的优化。
举个例子:
通过下图可以发现 cfunc 的符号确实没有了。 所有的 c 符号都会存在 mach-o 的 string table 中,如果符号字符串少了,理论上包大小自然也会有所减少。
3 、 __attribute__ (( naked )) : 此属性告诉编译器该函数是嵌入式程序集函数。可以使用 __asm 语句在汇编代码中编写函数体。
下面关于 naked 的使用举个例子:
在 func2 打断点。查看堆栈如下:
4 、 __attribute__ (( constructor )) 、 __attribute__ (( destructor )) 这两个属性还支持设置优先级: constructor ( priority ) destructor ( priority )
举个例子:
打印结果如下:
5 、 _attribute__ (( section ( "__DATA , __cfstring ”))) 开篇就有提到 _attribute__ (( section ( "__DATA , __cfstring ”))),在上一篇 常量字符串,你可能不知道的那些事儿~ 中,也有变量是用它来声明的。
下面就展开介绍下 section属性 :
举个例子 :
查看
mach-o
文件:
发现确实创建了一个自定义的 segement 和 section ,但是数据貌似只添加了一条。
注释掉第一行,仅保留第二行再试试看:
查看 .ll 文件
那一定是和“
used
”这个属性有关了。
没错!
used
就是告诉编译器,把变量标记为使用,而不去做优化。既然如此,那就使用一下
item2
这个变量吧。
添加代码:
重复上面的步骤,果真 .ll 文件中有 2 个记录了。
小总结下: 用 __attribute__ (( section (“ xx , xx ”)))在自定义的 section 中添加数据,然后再在运行时通过 getsectiondata ()这个方法,获取对应 section 的内容。这个小技巧还是可以用在很多场景的。
四、写在最后
之前对attribute就有所了解,但是没有相对系统的汇总过。 最近不知道写啥,把我知道的 __attribute__ 相关的这点东西梳理一下,如果有错误的话,可以私信我,一起探讨、学习、成长。
记得关注、转发、分享我,
下一期决定写一下最近在调研的内存相关的知识
!比心!~~
最近一段时间比较忙,在研究内存相关的东西,后续也会给大家分享下。距离上次更新已经一个月了,不知道写什么,在我上一篇文章中的一个细节,让我想起了 一个 知识点。下面就展开说说。有理解错误的地方欢迎私信指正 🙏 。
下面进入正题~~~
static__NSConstantStringImpl__NSConstantStringImpl__var_folders_l7_r5syspkx43724t__mz8h8rr00000gp_T_ViewController_754f19_mi_0__attribute__ ((section ("__DATA, __cfstring"))) ={ __CFConstantStringClassReference,0x000007c8,"abcd",4};
二、 __attribute__ 简介
__attribute__ 是一个用于在声明时指定一些特性的编译器指令,可以做一些错误检查和高级优化工作。
常见的几个__attribute__的参数:alias ("target")aligned (alignment)alloc_size (position-1, position-2)deprecated (msg)unavailable (msg)packedtls_model ("tls_model")visibility ("visibility_type")...这里不再展开(毕竟有些我也不清楚😂)
有兴趣的可以看: https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes
三、简单介绍几个 __attribute__ 参数 1 、 __attribute__ ( noreturn ):
2 、 __attribute__ (( visibility ( "hidden ”))): 这个属性 可以隐藏一些外部不需要的符号。这个对包大小都有一定程度的优化。
举个例子:
void cfunc(void) __attribute__((visibility("hidden"))) ;void cfunc2(){}
通过下图可以发现 cfunc 的符号确实没有了。 所有的 c 符号都会存在 mach-o 的 string table 中,如果符号字符串少了,理论上包大小自然也会有所减少。
3 、 __attribute__ (( naked )) : 此属性告诉编译器该函数是嵌入式程序集函数。可以使用 __asm 语句在汇编代码中编写函数体。
下面关于 naked 的使用举个例子:
void func2() {}void func() {func2();}int main(int argc, char * argv[]) {func();return 0;}
在 func2 打断点。查看堆栈如下:
void func() {__asm__ volatile ("STP x29, x30, [sp, #0x10]!;\n""B _func2;\n");}
在
func2
打断点。
查看堆栈如下:
4 、 __attribute__ (( constructor )) 、 __attribute__ (( destructor )) 这两个属性还支持设置优先级: constructor ( priority ) destructor ( priority )
举个例子:
__attribute__((constructor)) static void beforeMain(){printf("beforeMain\n");}__attribute__((destructor)) static void afterMain(){printf("afterMain\n");}int main(int argc, char * argv[]) {return 0;}
打印结果如下:
5 、 _attribute__ (( section ( "__DATA , __cfstring ”))) 开篇就有提到 _attribute__ (( section ( "__DATA , __cfstring ”))),在上一篇 常量字符串,你可能不知道的那些事儿~ 中,也有变量是用它来声明的。
下面就展开介绍下 section属性 :
举个例子 :
typedef struct SectionItem {int s1;int s2;}SectionItem_t;//用以下2种方式试试看。__attribute__((used, section("__MySeg,__MySec"))) static const SectionItem_t item1 = {123,456};static SectionItem_t item2 __attribute__ ((section ("__MySeg, __MySec"))) = {22,33};
发现确实创建了一个自定义的 segement 和 section ,但是数据貌似只添加了一条。
注释掉第一行,仅保留第二行再试试看:
//用以下2种方式试试看。//__attribute__((used, section("__MySeg,__MySec"))) static const SectionItem_t item1 = {123,456};static SectionItem_t item2 __attribute__ ((section ("__MySeg, __MySec"))) = {22,33};
发现 mach-o 文件中并没有自定义的 section 。 那一定是编译器搞了什么事情。
还是声明上面的item1和item2两 个变量 ,再 通过执行这个命令 再来 验证一下:
clang -S -fobjc-arc -emit-llvm main.m -o main.ll
查看 .ll 文件
那没道理啊,毕竟最开始有提到过,系统的常量字符串变量就是用的
__attribute__
((
section
(
"__DATA
,
__cfstring
”)))声明的啊~
添加代码:
int tmp = item2.s1;
重复上面的步骤,果真 .ll 文件中有 2 个记录了。
小总结下: 用 __attribute__ (( section (“ xx , xx ”)))在自定义的 section 中添加数据,然后再在运行时通过 getsectiondata ()这个方法,获取对应 section 的内容。这个小技巧还是可以用在很多场景的。
四、写在最后
之前对attribute就有所了解,但是没有相对系统的汇总过。 最近不知道写啥,把我知道的 __attribute__ 相关的这点东西梳理一下,如果有错误的话,可以私信我,一起探讨、学习、成长。