__attribute__
是一个编译器指令,其实是 GNU C
的一种机制,本质是一个编译器的指令,在声明的时候可以提供一些属性,在编译阶段起作用,来做多样化的错误检查和高级优化。用于在 C
、C++
、Objective-C
中修饰变量、函数、参数、方法、类等。
合理使用 __attribute__
有什么好处?
__attribute__
产生一些编译警告,使代码更规范。总之,__attribute__
起到了给编译器提供上下文的作用,如果错误的使用 __attribute__
指令,因为给编译器提供了错误的上下文,由此引起的错误通常很难被发现。
适用于方法、属性。告诉编译器已经过时,如果使用了,会报过时警告
常用写法:
// 可以自定义描述信息
__attribute__((deprecated("已过期!")))
// 系统的宏定义
DEPRECATED_ATTRIBUTE
使用场景:
在组件化、SDK
的时候,因为某个需求,我们升级了 API
,但是需要兼容老的版本,并且希望使用者调用最新的 API
。
// 标记这个属性已过期
@property (nonatomic, copy) NSString *name __attribute__((deprecated("属性已过期")));
// 标记方法已过期
- (void)testOld __attribute__((deprecated("方法已过期, 请使用 test2"))) {
}
- (void)testNew {
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// 编译器警告,'testOld' is deprecated: 已过期, 请使用 testNew
[self testOld];
// 编译器警告,提示 "'name' 已过期..."
NSLog(@"%@", self.name);
}
适用于方法、属性。告诉编译器不可用。如果使用了,就会编译失败,提示错误信息
常用写法:
// 可以自定义描述信息
__attribute__((unavailable("已经废弃,请使用 xxxx")))
// 系统宏定义
NS_UNAVAILABLE;
UNAVAILABLE_ATTRIBUTE;
使用场景:
自定义了 Class
的初始化方法,不希望外界使用 init
初始化,并且给出正确的提示。
比希望继续使用某个属性,并且给出正确的提示。
@interface ViewController : UIViewController
@property (nonatomic, copy) NSString *name __attribute__((unavailable("这个属性已经废弃")));
#pragma mark - 初始化
- (instancetype)init __attribute__((unavailable("这个方法已经废弃,请使用 initWithName:")));
- (instancetype)initWithName:(NSString *)Name;
@end
- (void) test {
// 编译不通过,提示 "'init' 已经废弃了..."
ViewController *vc = [[ViewController alloc] init];
// 编译不通过,提示 "'name' 已经废弃了..."
NSLog(@"%@", vc.name);
}
适用于 Class
。告诉编译器我不能有子类,类似 final
关键字
常用写法:
__attribute__((objc_subclassing_restricted))
使用场景:
#import <Foundation/Foundation.h>
__attribute__((objc_subclassing_restricted))
@interface ClangTest : NSObject
@end
#import "ClangTest.h"
// 这里编译出错,提示“Cannot subclass a class that was declared with the 'objc_subclassing_restricted' attribute”
@interface ClangSonTest : ClangTest
@end
适用于方法。告诉编译器子类重写这个方法的时候,必须调用[Super xxx]
常用写法:
// 通用写法
__attribute__((objc_requires_super))
// 系统宏定义,其实和上面是一样的
NS_REQUIRES_SUPER
使用场景:
#import <Foundation/Foundation.h>
@interface ClangTest : NSObject
- (void)instanceMethod1 __attribute__((objc_requires_super));
@end
#import "ClangTest.h"
@interface ClangSonTest : KDClangTest
@end
@implementation KDClangSonTest
- (void)instanceMethod1 {
NSLog(@"I am son");
// 这里编译器会出现警告: Method possibly missing a [super instanceMethod1] call
}
使用 constructor
属性修饰的函数能在 main()
函数之前执行,而使用 destructor
属性修饰的函数,在 main()
函数结束或 exit()
函数调用后执行。
// main之前调用
__attribute__((constructor))
使用场景:
__attribute__((constructor)) void before_main() {
printf("app before main\n");
}
__attribute__((destructor)) void after_main() {
printf("app after main\n");
}
int main(int argc, char * argv[]) {
printf("excute main\n");
return 0;
}
注意:
因为 load
是在 class
被加载的时候,就执行了,所以早于 constructor
。
所以顺序应该是:
load -> attribute((constructor)) -> main -> attribute((destructor)) -> initialize
https://nshipster.cn/attribute/