美文网首页iOS基本功
浅谈 __attribute__

浅谈 __attribute__

作者: s_在路上 | 来源:发表于2019-02-25 14:05 被阅读30次

    __attribute__ 是一个编译器指令,其实是 GNU C 的一种机制,本质是一个编译器的指令,在声明的时候可以提供一些属性,在编译阶段起作用,来做多样化的错误检查和高级优化。用于在 CC++Objective-C 中修饰变量、函数、参数、方法、类等。

    合理使用 __attribute__ 有什么好处?

    • 给编译器提供上下文,帮助编译器做优化,合理使用可以收到显著的优化效果。
    • 编译器会根据 __attribute__ 产生一些编译警告,使代码更规范。
    • 给代码阅读者提供必要的注解,助其理解代码意图。

    总之,__attribute__ 起到了给编译器提供上下文的作用,如果错误的使用 __attribute__ 指令,因为给编译器提供了错误的上下文,由此引起的错误通常很难被发现。

    deprecated

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

    常用写法:

    // 可以自定义描述信息
    __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);
    }
    

    unavailable

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

    常用写法:

    // 可以自定义描述信息
    __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);
    }
    

    objc_subclassing_restricted

    适用于 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
    

    objc_requires_super

    适用于方法。告诉编译器子类重写这个方法的时候,必须调用[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 与 destructor

    使用 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/

    相关文章

      网友评论

        本文标题:浅谈 __attribute__

        本文链接:https://www.haomeiwen.com/subject/snuwyqtx.html