美文网首页
iOS 中特殊变量定义

iOS 中特殊变量定义

作者: 大成小栈 | 来源:发表于2021-04-07 21:10 被阅读0次

    1. 关于宏

    预编译

    编译器在编译源码之前会进行预编译,在预编译之前也会进行一些操作,如:删除 反斜线+换行符的组合; 将各种形式的注释用空格替代等等。预编译在处理#define的时会从#开始,一直执行到遇到的第一个换行符(写代码的时候换行的作用)为止

    所以,正常情况下#define只会允许一行的宏定义。但是因为预编译之前会删除反斜线+换行符的组合,所以我们可以利用反斜线+换行符来定义多行宏,这样在预编译阶段的逻辑上#define定义就成了一行的宏了。

    #define在预处理阶段只进行文本的替换(相当于把代码拷贝粘贴),不会进行具体的计算,而且是直接替换(内联函数!!!),这个和函数有着很大的区别。

    // 宏(#define)的基本语法:
    #define 宏名 主体;
       ↓   ↓    ↓
    #define PI  3.1415926
    
    常见宏定义(#define)的形式
    // 类对象宏:类对象宏一般用来定义数据常量或字符串常量。
    #define PI   3.1415926
    #define NAME @"SunSatan"
    
    // 类函数宏:类函数宏就是宏名类似函数名,宏的主体为函数,
    // 并可以帮助主体函数接受参数,使用起来就像是函数一样。
    #define Log(x) NSLog(@"this is test: x = %@", x)
    

    类函数宏和函数的区别是, 类函数宏的参数不指定类型, 具体的参数类型在调用宏的时候由传入的参数决定,这就可能会遇到类型错误的问题。

    例:

    #define power(x) x*x
     
    int x = 2;
    int pow_1 = power(x);
    int pow_2 = power(x+1);
    
    // 猜猜pow_1和pow_2的值分别为多少?
    // 结果是pow_1=4 ,pow_2=5。
    

    这个结果是不是和预想的不一样,pow_2不应该等于9吗,为什么是5?
    注:宏真正的效果就是进行文本的替换

    宏(#define)中操作符的使用:

    # 为字符串化操作符,需要放置在参数前,将类函数宏中传入的参数用""括起来变成了c语言的字符串。

    #define Log(x) #x
    
    // 代码替换后:
    // Log(x) -> "x"
    

    ## 为符号连接操作符,需要放置在参数前,参数就会和##前面的符号连接起来。

    #define single(name) +(instancetype)share##name;
    
    // 代码替换后:
    // single(SunSatan) -> +(instancetype)shareSunSatan;
    

    2. 静态、常量、全局

    static

    用static来修饰一个变量时,它的生命周期、作用域会发生变化;其内存只被分配一次并存储到全局变量区,可供所有对象使用,其值在下次调用时仍维持上次的值。
    有时,我们为了避免重复定义一个变量,也可以使用static。例如tableView的cell重用机制中,我们会定义一个cellIdentifier ...

    • static修饰局部变量
      局部变量是存储在栈区的,一旦出了这个代码块,存储局部变量的这个栈内存就会被回收,局部变量也就被销毁。当用static修饰局部变量时,变量被称为静态局部变量,和全局变量静态全局变量一样,是存储在静态存储区。存储在静态存储区的变量,其内存直到 程序结束才会被销毁,生命周期是整个源程序。静态局部变量的作用域是声明它的代码块内。

    • static修饰全局变量
      当全局变量没有使用static修饰时,其存储在静态存储区,直到程序结束才销毁。也就是其作用域是整个源程序。我们可以使用extern关键字来引用这个全局变量。
      当全局变量使用static修饰时,其生命周期没有变,依旧是在程序结束时才销毁。但是其作用域发生变化,使用extern关键字无法引用这个全局变量(extern修饰的全局变量,一般定义在.m中,修饰在.h中。当然,.h中也是可以用static修饰变量的。)。

    // .m中,仅本文件可见,不可再被extern
    static int num;    
    
    @implementation 
    @end 
    
    const

    const修饰的是其右边的值,只可初始化时赋值一次,const右边的这个整体的值不能再改变,只可读。const 在*前/后的区别如下:

    // 两种写法等价,指针内容不可变
    const NSString *name = @"bac";
    NSString const * name = @"abc";
     // 指针地址不可变 
    NSString * const str = @"abc";
    
    extern

    主要是用来引用全局变量,它的原理是先在本文件中查找,查找不到再到其他文件中查找。

    //// .h中
    @interface PDConst : NSObject
    
    extern NSString *const appBaseURL;
    
    @end
    
    ////.m中
    @implementation PDConst
    
    NSString *const currentBaseURL = @"http://192....";
    
    @end
    

    3. 高级应用场景

    • static和const的联合使用
    //static和const联合使用是用来替代宏,把一个经常使用的字符串常量,定义成静态全局只读变量
    //使用const修饰key,表示key只读,不允许修改
    static NSString *const key = @“name”;
    //如果const修饰 *key1,表示 *key1 只读,key1还是能改变的
    static NSString const *key1 = @“myName”;
    
    • 宏定义与static、const的联合使用

    场景:封装一个三方库,其头文件中的代码如下,在引用了它的类中,我们能够使用DDLogVerbose(...)来打印log(当然三方库的其他文件中已经处理好了log的逻辑),那么我们应该怎么运行呢?

    #ifndef LOG_LEVEL_DEF
        #define LOG_LEVEL_DEF ddLogLevel
    #endif
    
    typedef NS_OPTIONS(NSUInteger, DDLogFlag){
        DDLogFlagError      = (1 << 0),
        DDLogFlagWarning    = (1 << 1),
        DDLogFlagInfo       = (1 << 2),
        DDLogFlagDebug      = (1 << 3),
        DDLogFlagVerbose    = (1 << 4)
    };
    
    
    typedef NS_ENUM(NSUInteger, DDLogLevel){
        DDLogLevelOff       = 0,
        DDLogLevelError     = (DDLogFlagError),
        DDLogLevelWarning   = (DDLogLevelError   | DDLogFlagWarning),
        DDLogLevelInfo      = (DDLogLevelWarning | DDLogFlagInfo),
        DDLogLevelDebug     = (DDLogLevelInfo    | DDLogFlagDebug),
        DDLogLevelVerbose   = (DDLogLevelDebug   | DDLogFlagVerbose),
        DDLogLevelAll       = NSUIntegerMax
    };
    
    #define SerLog(format,...) [ServiceLogManager customLogWithFormatString:[NSString stringWithFormat:format, ##__VA_ARGS__]]
    
    #define DDLogVerbose if (LOG_LEVEL_DEF & DDLogFlagVerbose) SerLog
    #define DDLogDebug   if (LOG_LEVEL_DEF & DDLogFlagDebug)   SerLog
    #define DDLogWarn    if (LOG_LEVEL_DEF & DDLogFlagWarning) SerLog
    #define DDLogInfo    if (LOG_LEVEL_DEF & DDLogFlagInfo)    SerLog
    #define DDLogError   if (LOG_LEVEL_DEF & DDLogFlagError)   SerLog
    
    

    在调用打log方法之前,如,在调用DDLogVerbose的文件内,我们首先要给定一个ddLogLevel变量:

    static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
    

    这样,三方库中的宏在被调用处,预编译时就能自动将LOG_LEVEL_DEF文本替换为ddLogLevel变量。若不给定这个变量会报这样的错误,Use of undeclared identifier 'ddLogLevel',你可能不理解ddLogLevel为什么定义在三方库外面,却被三方库自动读取到?

    替换的逻辑很简单:宏的替换动作不是在三方库源码内被进行的,而是在调用DDLogVerbose的位置,宏被展开,进而就直接在那个位置被替换掉。

    似乎据此思路,我们可以做一些看起来很奇妙的事情🤩!

    参考文章:
    https://blog.csdn.net/qq_36557133/article/details/86476686
    http://blog.sina.com.cn/s/blog_134451adb0102wfrn.html
    https://my.oschina.net/u/4410805/blog/3391545
    感谢!

    相关文章

      网友评论

          本文标题:iOS 中特殊变量定义

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