iOS_ __attribute__

2023-10-18 14:52:25 浏览数 (1)

__attribute__

__attribute__ 编译器属性机制,用于向编译器描述特殊得标识、检查或优化。 语法关键字是__attribute__紧跟2套圆括号,括号内是一个以逗号分隔的属性列表。 __attribute__指令被放在类前、函数/变量声明后面。

1.GNU C 的 __attribute__

属性分类:

1.类型属性(Type Attribute) aligned、packed、bitband:(见下文)

2.函数属性(Function Attribute) alias:设置别名 unused、used:(见下文) noinline、always_inline:(见下文) nonnull:(见下文) deprecated、constructor、destructor:(见下文) format、format_arg:(见下文) weak、section:(见下文) noreturn:表示没有返回,当遇到类似函数还未运行到return语句就需要退出来的情况,该属性可以避免出现错误信息。 flatten:声明被修饰函数内部调用的函数尽可能做内敛处理,具体还是要根据当前编译选项及上下文来定 pure:声明函数除了返回值外没有其他任何效果,仅依赖形参/全局对象。辅助编译器做消除公共子表达式和循环优化 const:类似 pure,但更严格。不能使用全局对象,参数不能是指针类型、往往不能调用非 const 属性的函数 sentinel:声明该可变参数函数的参数列表需要一个 NULL 结尾 malloc:声明函数返回的块不能包含任何指向其他对象的指针,帮助编译器估计哪些指针可能指向同一个对象 warn_unused_result:声明返回值很重要,当调用者未使用返回值时编译器发出警告⚠️ nothrow:不抛出C 异常

3.变量属性(Variable Attribute) alias:设置别名 aligned、packed:(见下文) unused、used、noinline:(见下文) deprecated、section(“name”):(见下文) weak、weakref(“target”):(见下文) visibility(“visibility_type”):(见下文) at(address):(见下文) zero_init:表示将未初始化的变量放到ZI数据节中。因为“NO_INIT”这显性命名的自定义节,具有UNINIT属性。

__has_attribute

__has_attribute 用来检测是否有该属性

代码语言:javascript复制
#if defined __has_attribute
#  if __has_attribute (nonnull)
#    define ATTR_NONNULL __attribute__ ((nonnull))
#  endif
#endif
used、unused

unused:该符号未被使用,编译器也不告警 used:声明该符号就算没有被使用也需要保留,release 环境下不会被优化

weak:两个或两个以上的同名全局符号(函数名或变量名),其中一个声明为 weak symbol 时,不会引发重定义错误。链接器会忽视弱符号,当其他符号不可用时才会使用。

weakref("target"):声明某个引用为弱引用,当需要引用的符号不存在也不会链接出错。必须配合 alias使用(即必须是 static 定义):

代码语言:javascript复制
__attribute__((weakref, alias("target")))
noinline、always_inline

noinline:声明未非内敛函数 always_inline:声明为内敛函数,不会被编译成函数调用,而是将实现直接 copy 到调用位置

deprecated
代码语言:javascript复制
// 带提示文案
__attribute((deprecated("use methodV2 instead")))
// 不带提示文案
__attribute((deprecated))

// 宏定义版本:
DEPRECATED_MSG_ATTRIBUTE("use methodV2 instead");
DEPRECATED_ATTRIBUTE

1.1 标记方法废弃:

代码语言:javascript复制
- (void)setupView __attribute((deprecated("use setupViewV2 instead")));

1.2 标记类废弃:

代码语言:javascript复制
__attribute__((deprecated))
@interface ViewController : UIViewController
@end

1.3 标记枚举废弃:

代码语言:javascript复制
typedef NS_ENUM(NSUInteger, MOTitleLineViewType) {
    MOTitleLineViewTypeLeft __attribute__((deprecated)),
    MOTitleLineViewTypeCenter
};
aligned、packed
  • aligned 用来调整内存对齐中每行的位数 。实现内存对齐,深度优化。
代码语言:javascript复制
// 如果设置少于4,编译器会自动优化成4
// 最大也只能是8
struct student {
    char sex;
    int length;
    char name[2];
    char value[16];
}__attribute__((aligned(16)));
  • packed:紧凑安排数据结构中的成员变量(跟aligned相反)
代码语言:javascript复制
struct zrecord {
char id;
int zar[32] __attribute__ ((packed));
};
constructor、destructor
代码语言:javascript复制
// 确保此函数在 在 main 函数被调用之前调用,在  load 之后 main 之前执行
__attribute__((constructor)) void funName() { }
// 确保此函数在 在 main 函数被调用之后调
__attribute__((destructor)) void funName() { }

// 在C/C  环境下work,还可以设置优先级参数(越小约高)
__attribute__((constructor(101))) // 1 ~ 100 为系统保留

constructordestructor 会在 ELF 文件中添加两个段 .ctors.dtors 。当动态库或程序在加载时,会检查是否存在这两个段,如果存在执行对应的代码。

nonnull

声明函数某些参数不能为空:

代码语言:javascript复制
extern void *
my_memcpy (void *dest, const void *src, size_t len) 
__attribute__((nonnull (1, 2)));
cleanup

__attribute__((cleanup(func))) 用于修饰一个变量,参数为一个方法。当该变量的作用域结束时自动执行该方法。

代码语言:javascript复制
static void stringCleanUp(__strong NSString **string) {
    NSLog(@"%@", *string);
}
NSString *string __attribute__((cleanup(stringCleanUp))) = @"momo";

Tips: cleanup是先于这个对象的dealloc调用的

修饰 block:

代码语言:javascript复制
static void blockCleanUp(__strong void(^*block)(void)) {
    (*block)();
}
// 加了个`unused`的attribute用来消除`unused variable`的warning
__strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^{
	NSLog(@"I'm dying...");
};

该 block 没有被显示的调用,但会在其 dealloc 前调用(即 被销毁前)

Reactive Cocoa 中的 @onExit 实现就是如此:

代码语言:javascript复制
#define onExit
    __strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^

例如:

代码语言:javascript复制
NSRecursiveLock *aLock = [[NSRecursiveLock alloc] init];
[aLock lock];
onExit {
    [aLock unlock]; // 妈妈再也不用担心我忘写后半段了
};
// doing need lock thing ...

该 block 被释放前会调用 unlock

format

声明属性有输出特征,编译时检查 函数声明 和 函数参数 之间得格式化字符串是否匹配。

  • 格式:format (archetype, string-index, first-to-check)
  • archetype:指定风格 printfscanf
  • string-index:第几个参数为格式化字符
  • first-to-check:从第几个参数开始,按上述规则进行检查
section

将声明的函数 或 数据 放入指定的段中,如:

代码语言:javascript复制
static void __attribute((section("__TEXT, MySection" ))) myFun1(void) {
    print("");
}
visibility

visibility 类型有 4 种:

  • default:默认可见性的对象与函数可以直接在其他模块中引用,包括在动态链接库中 ,它属于一个正常,完整的外部连接。
  • hidden:该符号不存放在动态符号表中,因此,其他可执行文件或共享库都无法直接引用它。使用函数指针可进行间接引用。
  • internal:除非由 特定于处理器的应用二进制接口 (psABI) 指定,否则,内部可见性意味着不允许从另一模块调用该函数。
  • protected:除非由 特定于处理器的应用二进制接口 (psABI) 指定,否则,内部可见性意味着不允许从另一模块调用该函数。
bitband、at(address)

bitband能有效地原子访问内存体系结构的SRAM和外围区域的单个位值,在某些存储器区域中,可以通过单个存储器访问 直接设置 或 清楚单个位。而不必使用传统的读取、修改、写入方法。 t(address)指定变量的绝对地址,变量被放置在自己的区域中,由编译器赋予适当的类型

代码语言:javascript复制
typedef struct {
	// ...
} structName __attribute__((bitband));
structName value __attribute__((at(0x20000040)));

2.objc

objc_subclassing_restricted

禁止衍生子类

代码语言:javascript复制
__attribute__((objc_subclassing_restricted))
@interface Person: NSObject
objc_requires_spuer

在父类中某个方法上添加这个,编译器会提醒子类的重写方法中调用 [super]

代码语言:javascript复制
__attribute__((objc_requires_spuer))
objc_runtime_name

用于 @interface@protocol,将类或协议的名字在编译时指定成另一个 格式:__attribute__((objc_runtime_name("<#OtherClassName#>")))

代码语言:javascript复制
 __attribute__((objc_runtime_name("OtherTest")))
 @interface Test : NSObject
 @end
 
 NSLog(@"%@", NSStringFromClass([Test class])); // "OtherTest"

这个属性可以用来做代码混淆


3.Clang特有的

availability

声明操作系统的版本的生命周期:

代码语言:javascript复制
__attribute__((availability(<#platform#>, <#introduced#>, <#deprecated#>, <#obsoleted#>, <#unavailable#>, <#message#>, <#strict#>, <#replacement#>, <#priority#>)))
  • platform:平台
  • introduced:声明被引入的第一个版本信息
  • deprecated:第一次不建议使用的版本,意味着使用者应该移除这个方法的使用
  • obsoleted:第一次被废弃的版本,意味着已经被移除,不能够使用了
  • unavailable:意味着这个平台不支持使用
  • message:提供一条文本消息,编译器会在发出相关使用已弃用或废弃声明的警告或错误时显示该消息。
  • replacement:可代替的api

还有一些宏可以使用,见:Foundation/NSObjCRuntime.h,如:

代码语言:javascript复制
- (void)setupView NS_AVAILABLE_IOS(7_0) // iOS7.0或之后才能使用
- (void)setupView NS_DEPRECATED_IOS(3_0, 8_0, "use setupViewV2 instead"); // iOS3启用,iOS8废弃
unavailable

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

代码语言:javascript复制
//系统的宏,可以直接拿来用
#define UNAVAILABLE_ATTRIBUTE __attribute__((unavailable))
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
// 例如:
- (instancetype)init NS_UNAVAILABLE;
overloadable

Clang在C中提供对C 标准函数重载的支持。函数重载在C中是通过overloadable属性引入的。例如:你可以重载tgsin函数,写出sin函数在入参不同时的不同版本。用于c语言函数,可以定义若干个函数名相同,但参数不同的方法,调用时编译器会自动根据参数选择函数原型。

代码语言:javascript复制
__attribute__((overloadable)) void print(NSString *string){
}
__attribute__((overloadable)) void print(int num){
}

Swift的@available

语法:

代码语言:javascript复制
@attribute name
@attribute name(attribute arguments)

语言/平台和操作系统:

指定某些Swift的语言版本/某些平台和操作系统版本的生命周期,名称有:

  • iOS
  • iOSApplicationExtension/macOS
  • macOSApplicationExtension
  • macCatalyst
  • macCatalystApplicationExtension
  • watchOS
  • watchOSApplicationExtension
  • tvOS
  • tvOSApplicationExtension
  • swift
  • 也可使用*来表明以上多有平台和语言都可用。

其他参数不要求顺序。

introduced

introduced指定平台语言``支持的第一个版本

代码语言:javascript复制
introduced: version number // 版本号由3个正整数组成

deprecated

deprecated指定平台语言``弃用的第一个版本

代码语言:javascript复制
deprecated: version number // 版本号由3个正整数组成,版本号也可以省略

obsoleted

obsoleted指定平台语言``废弃的第一个版本。当声明被废弃时,该声明会从指定平台或语言中删除且不能再使用。

代码语言:javascript复制
obsoleted: version number // 版本号由3个正整数组成

message

message提供一条文本消息,编译器会在发出相关使用已弃用或废弃声明的警告或错误时显示该消息。

代码语言:javascript复制
message: messaga

renamed

renamed指定已重命名的新名称,编译器在发出有关使用重命名声明的错误时显示新名称,并提供一键fix。

代码语言:javascript复制
renamed: new name // 新名称-字符串

unavailable

unavailable声明为不可用

代码语言:javascript复制
@available(*, unavailable, rename: "setupViewsV2")
func setupView() { }

举例

代码语言:javascript复制
@available(*, deprecated, message: "use setupViewV2 instead")
func setupView() { }

// renamed标记新接口,warning可一键fix
@available(*, deprecated, renamed: "setupViewV2")
func setupView() { }

// obsoleted 标记禁用版本
// swift4.1 之后的版本会报错,强制使用新接口
@available(swift, obsoleted: 4.1, renamed: "setupViewV2")
func setupView() { }

// 也可以联合使用
@available(swift, deprecated: 4.0, obsoleted: 5.0, message: "This will be removed in v5.0; please migrate to a different API.")
func setupView() { }

参考: How to deprecate a method in Xcode Attributes attribute iOS 编译器__Attribute__的入门指南 黑魔法__attribute__((cleanup)) linux 中__attribute__ 机制详解 __attribute__详解及应用

0 人点赞