美文网首页
关于#define/extern/static的思考与总结

关于#define/extern/static的思考与总结

作者: 无神 | 来源:发表于2019-05-24 07:23 被阅读0次
    傍晚的海边

    写在前面

    全局常量作为开发人员一定是一个比较熟悉的概念。全局常量的写法自然也比较多,最近在进行项目的常量重构时看到了各种各样的写法,其中宏定义占大部分,然而有很多使用宏定义是不规范的,而且宏定义只是在预编译阶段进行文本替换,不进行类型检查,从网上看到大量使用宏定义会拖慢编译速度。

    所以在定义全局常量时,为了提高开发过程中的规范度和编译速度,宏定义并不是最佳选择。所以我重构的原则是:

    能声明成外部常量的,尽量声明成外部常量,万不得已的才使用宏定义。

    iOS常量声明(OC)

    一、宏

    计算机科学里的宏(Macro),是一种批量处理的称谓。一般说来,宏是一种规则或模式,或称语法替换 ,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串)。这种替换在预编译时进行,称作宏展开。

    在我刚刚接触开发的时候,我学习到的定义全局常量的方法就是宏。由于宏只是做字符串的替换,它还是有它的优势的。我们可以使用它来一些常量函数

    例子:
    1、定义屏幕相关的常量。

    /屏幕宽高,frame,bounds,size
    #define kBKScreenWidth          [[UIScreen mainScreen] bounds].size.width
    #define kBKScreenHeight         [[UIScreen mainScreen] bounds].size.height
    #define kBKScreenBounds         [UIScreen mainScreen].bounds
    #define kBKScale                [[UIScreen mainScreen] scale]
    

    2、定义调试的log输出函数。

    #pragma mark - DEBUG
    #ifdef DEBUG
    // 定义是输出Log
    #define DLog(format, ...) NSLog(@"Line[%d] %s " format, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)
    #else
    // 定义是输出Log
    #define DLog(format, ...)
    #endif
    

    从上面的示例可以看出宏定义的关键字是#define.
    宏定义常量的公式:

    #define constantA statementA
    预编译的时候使用constantA部分的内容替换成statementA
    对于函数的定义则稍微复杂一些,有参数和无参数。无参数的函数是直接进行字符串的替换,有参数的还要进行参数的替换。

    二、extern

    使用extern关键字声明全局常量,这个应该算是最标准的做法了。这个是后面在网上的帖子中有看到,当然开源代码中也看到过,确定无疑是定义全局常量的最佳选择。

    extern定义全局常量分为声明部分和赋值部分,分别放在 .h & .m文件中。

    代码示例:

    • UserInfoModelConstants.h
    extern NSString *const BKUSER_AGE_KEY         ;
    extern NSString *const BKUSER_TELPHONE_KEY    ;
    extern NSString *const BKUSER_ADDRESS_KEY     ;
    extern NSString *const BKUSER_BRIEF_KEY       ;
    
    • UserInfoModelConstants.m
    NSString *const BKUSER_AGE_KEY         =     @"XXXXX.userAge";
    NSString *const BKUSER_TELPHONE_KEY    =     @"XXXXX.telphoneNO";
    NSString *const BKUSER_ADDRESS_KEY     =     @"XXXXX.address"; 
    NSString *const BKUSER_BRIEF_KEY       =     @"XXXXX.brief";
    

    特别提示:

    switch-case中使用的常量不能使用上述方法定义,因为switch-case在编译的使用就要知道常量的值。上述方式定义的常量在编译阶段不知道具体值,编译报错。

    对于switch-case表达式中的常量推荐使用枚举或者宏定义

    三、UIKIT_EXTERN

    对于UIKIT_EXTERN用法和extern完全一样。把extern替换成UIKIT_EXTERN即可。

    下方是UIKIT_EXTERN的系统宏定义。

    #ifdef __cplusplus
    #define UIKIT_EXTERN        extern "C" __attribute__((visibility ("default")))
    #else
    #define UIKIT_EXTERN            extern __attribute__((visibility ("default")))
    #endif
    
    

    UIKIT_EXTERN,是经过处理的extern。

    简单来说:就是将函数修饰为兼容以往C编译方式的、具有extern属性(文件外可见性)、public修饰的方法或变量库外仍可见的属性。

    四、FOUNDATION_EXTERN

    下方是FOUNDATION_EXPORT的宏定义,内部使用的就是extern,只不过多做了 C++ 的兼容。用法和extern也是一样的。

    #if defined(__cplusplus)
    #define FOUNDATION_EXTERN extern "C"
    #else
    #define FOUNDATION_EXTERN extern
    #endif
    

    五、FOUNDATION_EXPORT

    FOUNDATION_EXPORT的宏定义如下:

    #ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #else
    #ifndef FOUNDATION_EXPORT
    #if defined(__cplusplus)
    #define FOUNDATION_EXPORT extern "C"
    #else
    #define FOUNDATION_EXPORT extern
    #endif
    #endif
    #endif
    

    用法也和extern类似,不过这种方式见到的比较少,可以忽略。

    六、static

    static也可以声明全局常量,static 声明全局常量的方法相比上面的几种更简单一些。
    示例代码:

    static NSString *const makeCrashAlertTitle = @"制造一个 Crash ?";
    static NSString *const fixCrashAlertTitle = @"提示";
    static NSString *const fixCrashButtonTitle = @"修复";
    static NSString *const cancelButtonTitle = @"取消";
    static NSString *const createCrashButtonTitle = @"制造Crash!";
    static NSString *const mainStoryboardInfoKey = @"UIMainStoryboardFile";
    

    static声明常量的方式,一般用于少量的局部常量。

    外部常量、宏、static声明的常量的区别。

    方式 #define extern static
    原理 字符串替换 声明常量 声明常量
    作用域 可以全局访问 可以全局访问 局部的、或者只有声明文件本身可以访问
    是否开辟内存 不开辟 开辟 开辟
    是否进行编译检查

    七、总结

    对于常量的声明一共有6种方式,那么我们应该怎么使用呢?

    以下仅代表个人看法,不吝赐教!!!

      1. 能使用extern/UIKIT_EXTERN/FOUNDATION_EXTERN定义成外部常量的,尽量使用extern/UIKIT_EXTERN/FOUNDATION_EXTERN定义成外部常量。
      1. 不兼容C++情况下,直接使用extern声明即可。
      1. #define一般主要用于定义一些函数,和extern替代不了的常量。
      1. 必须兼容C++的情况下,UIKIT_EXTERN/FOUNDATION_EXTERN这两种如何使用区分呢,从别的文章上看到的说明大概意思是这样的:你声明的变量如果是UIKIT框架中定义的,就使用UIKIT_EXTERN代替extern兼容C++;如果是声明的变量是FOUNDATION框架中定义的,就使用FOUNDATION_EXTERN代替extern兼容C++。
      1. 至于static主要用于定义局部常量。

    相关文章

      网友评论

          本文标题:关于#define/extern/static的思考与总结

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