__inline__ 是否会改变 local static variable 的行为?
内容提要
本文讨论了使用__inline__和__attribute__((always_inline))修饰函数时,对函数中的static变量的影响。文章指出,__inline__和__attribute__((always_inline))并不会改变static变量的行为,因为编译器会决定是否进行内联,应该将NS_INLINE函数视为静态函数。文章建议尽量不要在头文件中声明静态函数,除非清楚其用途。
延伸解读
static 函数与普通函数的 static 变量作用域差异
文章通过汇编分析指出,static 函数内的局部 static 变量在每个 Compile Unit 中都会生成一份独立副本,即使函数被内联也是如此。而普通函数(非 static)的局部 static 变量在所有 Compile Unit 中共享同一份。这一差异源于 static 函数具有内部链接属性,其变量为 hidden global,而普通函数为外部链接。理解这一点有助于避免因误用 static 函数导致单例逻辑失效。
NS_INLINE 的隐式 static 属性及其风险
NS_INLINE 在非 MSVC 环境下通常定义为 static __inline__ __attribute__((always_inline)),因此被其修饰的函数默认具有 static 属性。这意味着函数内的 static 变量会在每个包含该头文件的 Compile Unit 中独立存在。若该变量用于 dispatch_once 等单次初始化逻辑,则可能被多次执行,破坏预期行为。文章建议尽量避免在头文件中声明 static 函数,除非明确其影响。
__inline__ 的真实语义与链接行为
文章澄清 __inline__ 并非简单的宏展开,而是允许函数在多个 Compile Unit 中重复定义,链接器会任选一份并丢弃其余,从而避免多重定义错误。若内联失败,函数可能变为 weak symbol,仍不会引发链接错误,但可能被强符号替换。因此,将 __inline__ 理解为“唯一化”或“弱符号”更准确,而非强制内联。
正确使用内联函数的建议
若希望函数内的 static 变量全局唯一,应将其定义为普通函数(非 static),并可选择加上 __inline__ 或 __attribute__((always_inline))。若必须使用 static 函数,则应将其定义在 .m 文件中,确保只在一个 Compile Unit 内可见。文章强调,是否内联应由编译器决定,不应将 __inline__ 与宏展开混为一谈。
Q&A
__inline__ 和 __attribute__((always_inline)) 对 static 变量的行为有什么影响?
__inline__ 和 __attribute__((always_inline)) 不会改变 static 变量的行为,编译器决定是否进行内联。
为什么不建议在头文件中声明 static 函数?
在头文件中声明 static 函数会导致每个引入该头文件的编译单元都有一份副本,可能导致包大小增长和多重定义问题。
同一个 Compile Unit 中的 static 函数的 static 变量是如何共享的?
在同一个 Compile Unit 中,static 函数的 static 变量是共享的,只有一份副本。
不同 Compile Unit 中的 static 函数的 static 变量有什么区别?
在不同 Compile Unit 中,static 函数的 static 变量不是共享的,每个 Compile Unit 都有一份自己的副本。
使用 __inline__ 可以避免什么问题?
使用 __inline__ 可以避免链接时的多重定义错误,但不保证内联成功。
NS_INLINE 函数的默认属性是什么?
NS_INLINE 函数在正常情况下默认为 static 属性。