iOS const、宏、static、extern的關係

一、const 的介紹和基本使用以及使用場景

  • 1.1、const 簡介:經常使用的字符串常量,一般是抽成宏,但是蘋果不推薦我們抽成宏,推薦我們使用const常量。

  • 1.2、const 作用:限制類型

    • const 僅僅用來修飾右邊的變量(基本數據變量p,指針變量*p),被const修飾的變量是隻讀的。如下
      • const 用法一 (修飾基本變量p)
        不使用const修飾基本變量,允許修改值

        int a = 10;
        a = 12;
        NSLog(@"a=%d",a);
        打印結果:a=12
        

        使用const修飾基本變量

        //這兩種寫法是一樣的,const只修飾右邊的基本變量 b
        const int b = 5; // b:只讀變量
        int const b = 5; // b:只讀變量
        // 由於b是隻讀的,b無法被修改,入下代碼會報錯
        b = 3 // 報錯,b無法修改
        
      • const 用法二 (修飾指針變量 *p,帶*的變量,就是 指針變量)
        不使用const修飾指針變量

        // 修飾指針變量 *p,帶 * 的變量,就是指針變量
        // 定義一個指向int類型的指針變量,指向a的地址
        a = 12;
        int *p = &a;
        int c = 7;
        p = &c;
        NSLog(@"p=%d",*p);
        打印結果:p=8
        
        // 由於 p 沒有被修飾,它訪問 內存空間的值 和 指向的地址 都可以被修改允許修改
        *p = 11;
        NSLog(@"p=%d",p);
        打印結果:p=11
        

        使用 const 修飾指針變量,const 修飾指針變量訪問的內存空間,修飾的是右邊東西,如下 8 種情況來分析

        // 1、2、4 的效果一樣 都是修飾 const右邊的 *q,3修飾的是變量 q ,切記 const修飾的是右邊的
        int const *q = 7;   // 1
        const int *q = 7;   // 2
        int * const q = 7;  // 3
        const int *q = 7;   // 4
        // 首先下面的 q 都被修飾,也就是q不能被賦值,然後 * const q 又被 const 修飾
        int const * const q = 7;  // 5
        const int * const q = 7;  // 6
        const int * const q = 7;  // 7
        const int * const q = 7;  // 8
        

        提示:

        • 1、2、4 的效果一樣 都是修飾 const右邊的 *q3 修飾的是變量 q ,切記 const 修飾的是右邊的
        • 首先下面的 q 都被修飾,也就是 q 不能被賦值,然後 * const q 又被 const 修飾
  • 1.3、const 的使用場景(場景一用的居多)

    • 場景一:修飾全局變量,目的是:使外界無法修改變量,保持只讀,提高預編譯的速度和時間(蘋果建議使用 const),如下:

      // 設置基礎的url,這樣來保證base_url的不變(封裝請求的類)
      NSString * const base_url =  @"http://www.baodu.com/";
      
    • 場景二:修飾方法中的參數,如下

      -(void)constTest2{
           [self test:@"你好!"];
           int p = 1;
           [self test1:&p];
           [self test2:2];
      }
      
      // 當一個方法的參數,只讀.
      -(void)test:(NSString * const)string{
           // 這句代碼是報錯的,被 const 修飾過後,string 是無法被修改的
           string = @"234";
      }
      // 指針只讀,不能通過指針修改值
      - (void)test1:(int const *)a{
           //  *a = 11;
      }
      
      // 基本數據類型只讀
      - (void)test2:(int const)a{
      
      }
      

二、宏 的簡單使用

  • 2.1、基本概念:宏是一種批量處理的稱謂。一般說來,宏是一種規則或模式,或稱語法替換 ,用於說明某一特定輸入(通常是字符串)如何根據預定義的規則轉換成對應的輸出(通常也是字符串)。這種替換在預編譯時進行,稱作宏展開。編譯器會在編譯前掃描代碼,如果遇到我們已經定義好的宏那麼就會進行代碼替換,宏只會在內存中copy一份,然後全局替換,宏一般分爲對象宏和函數宏,推薦博客

  • 2.2、宏的弊端:如果代碼中大量的使用宏會使預編譯時間變長。

  • 2.3、常用宏舉例如下

    /** 1、屏幕的寬高 */
    #define JK_SCREEN_WIDTH  [UIScreen mainScreen].bounds.size.width
    #define JK_SCREEN_HEIGHT  [UIScreen mainScreen].bounds.size.height
    /** 2、判斷是不是蘋果手機 */
    #define JKIs_Iphone (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
    
  • 2.4、const與宏的區別?

    答:1.編譯時刻 宏:預編譯 const:編譯;2.編譯檢查 宏沒有編譯檢查,const有編譯檢查;3.宏的好處 定義函數,方法 const不可以;4.宏的壞處 大量使用宏,會導致預編譯時間過長

    提示:

    • 預編譯:在打開項目的時候上面會有一個加載項目的進度條就是預編譯
    • 編譯:command+R和command+B都是編譯
    • 網上的誤區:大量使用宏,會導致內存暴增(定義一個字符串的宏,賦值給多個變量,打印其內存地址,經過測試:宏定義的是常量,常量都放在常量區,只會生成一份內存,故網上說的都是不對的),如下圖

三、static 簡單使用

  • 3.1、修飾局部變量

    • <1>、被static修飾局部變量,延長生命週期,跟整個應用程序有關,程序結束纔會銷燬,如下:在一個類的裏面打印下面的方法,只要程序不銷燬, a 的值就不會被銷燬,會一直保持最後一次給 a 賦的值

      -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
      
            static int a = 0;
            ++a;
            NSLog(@"a=%d",a);
      }
      
    • <2>、被 static 修飾局部變量,只會分配一次內存,如下:從打印結果我們可以看到,a 的內存地址不會再變

      static int a = 0;
      ++a;
      NSLog(@"a = %d a的內存地址=%p",a,&a);
      部分打印結果:
      a = 1   a的內存地址=0x10e758160
      a = 2   a的內存地址=0x10e758160
      a = 3   a的內存地址=0x10e758160
      a = 4   a的內存地址=0x10e758160
      

      提示:被static修飾局部變量什麼時候分配內存?程序一運行就會給static修飾變量分配內存

  • 3.2、修飾全局變量,被static修飾全局變量,作用域會修改,也就是只能在當前文件下使用

    #import "ViewController.h"
    
    static int b = 20;
    
    @interface ViewController ()
    
    @end
    
    @implementation TestViewController
    
    - (void)viewDidLoad {
         [super viewDidLoad];
    
        self.view.backgroundColor = [UIColor purpleColor];;
    }
    @end
    

四、extern 簡單使用

  • 4.1、聲明外部全局變量(只能用於聲明,不能用於定義),舉例如下:我們在類裏面定義的全局變量,在其他的類裏面使用的時候只要聲明一下就好

    #import "ViewController.h"
    int x = 20;
    
    @interface ViewController ()
    
    @end
    
    @implementation TestViewController
    
    - (void)viewDidLoad {
          [super viewDidLoad];
    
          self.view.backgroundColor = [UIColor purpleColor];;
    }
    @end
    

    在其他類裏面使用一,如下:聲明一下即可

    #import "TestViewController.h"
    
    @interface TestViewController ()
    
    @end
    
    @implementation TestViewController
    
    - (void)viewDidLoad {
          [super viewDidLoad];
    
          self.view.backgroundColor = [UIColor purpleColor];;
    }
    
    extern int x;
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
         NSLog(@"x的值是:%d",x);
         // 打印結果: x的值是:20
    }
    @end
    

    在其他類裏面使用二,如下:在ViewController類的.h裏面聲明一下即可,如下:

    #import <UIKit/UIKit.h>
    
    @interface ViewController : UIViewController
    
    extern int x;
    
    @end
    
  • 4.2、extern工作原理:先會去當前文件下查找有沒有對應全局變量,如果沒有,纔會去其他文件查找

五、static 與 const 聯合使用

  • 5.1、回顧一下 static 與 const
    const:修飾全局變量
    static:修飾全局變量,修改作用域

  • 5.2、static 與 const 聯合使用
    如果我們想這個 BASE_URL無法被其他類使用,那麼我們就在前面加上 static 因爲 static 修飾全局變量,修改作用域,只能在 UrlTest裏面使用,再其他類裏面使用是不可以的,切記:這個 BASE_URL 是在 .m 裏面定義的

    #import "UrlTest.h"
    
    static NSString * const BASE_URL  =  @"http://www.baodu.com/";
    
    @implementation UrlTest
    
    @end
    

六、extern 與 const 聯合使用

  • 6.1、開發中使用場景:在多個文件中經常使用的同一個字符串常量,可以使用extern與const組合。原因入下:

    • static與const組合:在每個文件都需要定義一份靜態全局變量。
    • extern與const組合:只需要定義一份全局變量,多個文件共享。

    提示:開發中便於管理所有的全局變量,通常搞一個Global文件,裏面專門定義全局變量,統一管理,要不然項目文件太多不好找。

  • 6.2、extern的基本使用 :當我們在一個防止一個變量被修改的時候,我們在前面加上 const,如下,僅僅是 BASE_URL無法被修改,在自己的.h文件裏面 extern 聲明一下即可,在其他類裏面通過 導入 .h 文件,還是可以使用的 BASE_URL

    • UrlTest的.h文件 ( 聲明 BASE_URL )

      #import "UrlTest.h"
      // 聲明  BASE_URL
      extern NSString * const BASE_URL;
      
      @implementation UrlTest
      
      @end
      
    • UrlTest的.m文件

      #import "UrlTest.h"
      
      NSString * const BASE_URL  =  @"http://www.baodu.com/";
      
      @implementation UrlTest
      
      @end
      

      提示:定義全局的東西,遵循規定,頂一個以全局的文件來管理全局變量,以避免全局變量重複定義

  • 6.3、extern 的高級使用 (模仿 YYKIT 的使用 ),其實它只是把蘋果的宏拿過來改改名字,看起來很牛逼,我們也可以牛逼一下,如下:

    • 蘋果的定義:

      #ifdef __cplusplus
      #define UIKIT_EXTERN     extern "C" __attribute__((visibility ("default")))
      #else
      #define UIKIT_EXTERN         extern __attribute__((visibility ("default")))
      #endif
      
    • 我們只需要把 UIKIT_EXTERN 改爲 JKKIT_EXTERN,那以後我們就可以使用我們自己定義的

      #ifdef __cplusplus
      #define JKKIT_EXTERN        extern "C" __attribute__((visibility ("default")))
      #else
      #define JKKIT_EXTERN            extern __attribute__((visibility ("default")))
      #endif
      
    • 蘋果和我們自己定義的使用如下

      #import <Foundation/Foundation.h>
      #import <UIKit/UIKit.h>
      
      #ifdef __cplusplus
      #define JKKIT_EXTERN        extern "C" __attribute__((visibility ("default")))
      #else
      #define JKKIT_EXTERN            extern __attribute__((visibility ("default")))
      #endif
      
      // 使用自己定義的
      JKKIT_EXTERN NSString * const BASE_URL;
      // 使用蘋果定義的 UIKIT_EXTERN
      // UIKIT_EXTERN NSString * const BASE_URL;
      
      NS_ASSUME_NONNULL_BEGIN
      
      @interface UrlTest : NSObject
      
      @end
      
      NS_ASSUME_NONNULL_END
      
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章