老青菜

iOS __attribute__

2018-09-17

attribute 是编译器属性,属于GNU C特色之一,用于向编译器描述特殊的标识、检查或优化,在iOS系统头文件里,经常可以看到 __attribute__(xxx)

书写格式:

__attribute__(xxx) 
__attribute__((deprecated("已经过期!"))) 
__attribute__((unavailable)) 
__attribute__((availability(xxx))) 
__attribute__((cleanup)) 
__attribute__((objc_subclassing_restricted)) 
__attribute__((objc_requires_super)) 
__attribute__((warn_unused_result)) 
__attribute__((constructor)) 
......

我们来看一下几个主要的使用场景。

deprecated

适用于方法、属性。告诉编译器已经过时,如果使用了,会报过时警告

常用写法:

//可以自定义描述信息
__attribute__((deprecated("已过期!")))

//系统的宏定义
DEPRECATED_ATTRIBUTE 

使用场景:

在组件化、做SDK的时候,因为某个需求,我们升级了API,但是需要兼容老的版本,并且希望使用者调用最新的API。

简单使用:

//标记这个属性已过期
@property (nonatomic, strong) 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);
}

unavailable

适用于方法、属性。告诉编译器不可用。如果使用了,就会编译失败,提示错误信息

常用写法:

//可以自定义描述信息
__attribute__((unavailable("已经废弃,请使用 xxxx")))


//系统宏定义
NS_UNAVAILABLE;
UNAVAILABLE_ATTRIBUTE;

使用场景:

  • 自定义了Class的初始化方法,不希望外界使用init初始化,并且给出正确的提示。
  • 比希望继续使用某个属性,并且给出正确的提示。

简单使用:

@interface ViewController : UIViewController
@property (nonatomic, strong) 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);
}

availability

适用于方法。告诉编译器方法在某个平台适用的版本

常用写法:

__attribute__((availability(ios,introduced=6.0,deprecated=8.0,obsoleted=10.0,message="ios10.0中废弃")));

ios:iOS平台
introduced:开始使用的版本
deprecated:开始弃用的版本
obsoleted:禁止使用的版本

cleanup

适用于变量。当这个变量作用域结束时,调用指定的函数

常用写法:

__attribute__((cleanup(作用域结束调用的函数名)))

简单使用:

void intCleanup(int *value){
    NSLog(@"cleanup:%d",*value);
}

void stringCleanup(__strong NSString **value){
    NSLog(@"cleanup:%@",*value);
}

void rectCleanup(CGRect *value){
    CGRect temp = *value;
    NSString *str = NSStringFromCGRect(temp);
    NSLog(@"cleanup:%@",str);
}

void appDelegateCleanup(__strong AppDelegate **value){
    NSLog(@"cleanup:%@",*value);
}

int main(int argc, char * argv[]) {
    //第一个作用域
    {
        int a __attribute__((cleanup(intCleanup))) = 10;
    }
    //第二个作用域
    {
        NSString *string __attribute__((cleanup(stringCleanup))) = @"张三";
        CGRect rect __attribute__((cleanup(rectCleanup))) = {0,0,1,1};
    }
    //第三个作用域
    {
        AppDelegate *delegate __attribute__((cleanup(appDelegateCleanup))) = [[AppDelegate alloc] init];
    }
}


//控制台输出
1.xxx[6549:886516] cleanup:10
2.xxx[6549:886516] cleanup:{{0, 0}, {1, 1}}
3.xxx[6549:886516] cleanup:张三 //用一个作用域,这一个cleanup 是最先加的
4.xxx[6780:893605] cleanup:<AppDelegate: 0x6040000337a0>
5.xxx[6780:893605] AppDelegate dealloc //dealloc 在 cleanup之后

结论:

1.一个作用域内有若干个cleanup的变量,他们的调用顺序是先入后出的栈式顺序
2.调用时机:cleanup比dealloc早
3.作用域的结束包括:大括号结束、return、goto、break、exception等

objc_subclassing_restricted

适用于Class。告诉编译器我不能有子类,类似 final 关键字

常用写法:

__attribute__((objc_subclassing_restricted))

简单使用:

#import <Foundation/Foundation.h>

__attribute__((objc_subclassing_restricted))
@interface KDClangTest : NSObject
@end


#import "KDClangTest.h"

//这里编译出错,提示“Cannot subclass a class that was declared with the 'objc_subclassing_restricted' attribute”
@interface KDClangSonTest : KDClangTest
@end

objc_requires_super

适用于方法。告诉编译器子类重写这个方法的时候,必须调用[Super xxx]

常用写法:

//通用写法
__attribute__((objc_requires_super))

//系统宏定义,其实和上面是一样的
NS_REQUIRES_SUPER

简单使用:

#import <Foundation/Foundation.h>

@interface KDClangTest : NSObject

- (void)instanceMethod1 __attribute__((objc_requires_super));
@end


#import "KDClangTest.h"

@interface KDClangSonTest : KDClangTest
@end
@implementation KDClangSonTest


-(void)instanceMethod1 {
    NSLog(@"I am son");
    //这里编译器会出现警告: Method possibly missing a [super instanceMethod1] call
}

warn_unused_result

适用于方法(有返回值的)。告诉编译器这个方法被调用之后,需要检查返回值有没有被使用

常用写法:

__attribute__((warn_unused_result))

简单使用:


- (BOOL) test __attribute__((warn_unused_result)) {
    return YES;
}

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self test];
    //这里编译器警告: Ignoring return value of function declared with 'warn_unused_result' attribute
}

constructor

告诉编译器在main之前或者之后调用某个方法

常用写法:

//main之前调用
__attribute__((constructor))

//main之后调用
__attribute__((destructor))

简单使用:

__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");
}

注意:

因为load是在class被加载的时候,就执行了,所以早于constructor 。
所以顺序应该是:
load -> attribute((constructor)) -> main -> attribute((destructor)) -> initialize

参考链接


sunnyxx - attribute黑魔法
Mattt - attribute

Tags: objc
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章