1. 程式人生 > 實用技巧 >iOS 常用關鍵字 static、const、extern、define

iOS 常用關鍵字 static、const、extern、define

static

static對變數的修飾在編譯階段執行,被static修飾的變數在編譯階段會進行編譯檢查,會報編譯錯誤。

被static修飾的變數僅在編譯階段初始化一次,在全域性/靜態區為它分配一份記憶體,一直到程式結束執行由系統回收。

修飾區域性變數

  1. 延長區域性變數的生命週期(儲存區域從棧移動到靜態區), 程式結束才會銷燬。
  2. 區域性變數只會生成一份記憶體, 不管方法執行多少次, 其只會初始化一次。

例:在一個類的裡面列印下面的方法,只要程式不銷燬, a 的值就不會被銷燬,會一直保持最後一次給 a 賦的值,記憶體地址不會再變

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    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

修飾全域性變數

  1. 被static修飾全域性變數,作用域會修改,也就是隻能在當前檔案下使用

沒有使用static關鍵字修飾(不管是在.m還是.h中宣告)的全域性變數,在其他.m和.h檔案中定義同名的全域性變數,在編譯時是會報重複宣告錯誤的,也就是此時的全域性變數的作用域是整個專案。

而使用static關鍵字修飾(在.m中宣告)的全域性變數後,在其他.m和.h檔案中定義同名的全域性變數就不會報錯了,因此我們得到的上述第三點作用。(PS:如果使用static關鍵字修飾(在.h中宣告)的全域性變數,只要在檔案中#import該.h檔案,還是可以使用該全域性變數,所以第三點作用強調的是(在.m中宣告)的全域性變數)

static的用法:

//只有以下兩種用法,且效果一樣
static NSString *name_1 = @"SunSatan";
NSString static *name_2 = @"SunSatan";

全域性變數是不安全的,因為它可能會被外部修改,所以在.m中定義全域性變數時推薦使用static關鍵字修飾。

const

const對變數的修飾在編譯階段執行,被const修飾的變數在編譯階段會進行編譯檢查,會報編譯錯誤。

被const修飾的變數僅在編譯階段初始化一次,在常量區為它分配一份記憶體,一直到程式結束執行由系統回收。

const 作用

  1. 將位於const右部的變數修飾為常量
  2. 被const修飾的變數是隻讀的,不能被修改

const的用法:

1. 修飾基本變數

// 對於基礎資料型別且不加*來說,這兩種寫法是一樣的,
// const只修飾右邊的intVar,讓intVar為常量且只讀
// intVar的值不可以被修改
const int intVar = 1;
int const intVar = 1;

2. 修飾指標變數

const NSString *p = @"Satan";  //  *p只讀 ; p變數
NSString const *p = @"Satan";  //  *p只讀 ; p變數

NSString * const p = @"Satan"; //  *p變數 ; p只讀

const NSString * const p = @"Satan"; //  *p只讀 ; p只讀
NSString const * const p = @"Satan"; //  *p只讀 ; p只讀

觀察const右邊緊跟著的是 * 還是varName,只有是const 右邊緊跟varName時,varName才變為常量且無法被修改。

extern

extern關鍵字修飾全域性變數是表示對該全域性變數的訪問,而不是定義該全域性變數,所以並不會分配記憶體。

extern關鍵字會先在當前檔案查詢有沒有該全域性變數,沒有找到,才會去整個專案中的檔案去查詢。

extern的作用:

可以使用extern關鍵字訪問全域性變數,前提是該全域性變數沒有static關鍵字修飾。

extern的用法:

//正確寫法要分兩步
extern NSString *name_1;//這一步是表示訪問
 
name_1 = @"不是SunSatan";//這一步才能修改
 
 
//下面寫法是錯誤的
extern NSString *name_1 = @"不是SunSatan";

extern也可以使用蘋果官方定義的巨集:UIKIT_EXTERN來進行替換,你看哪個你覺得舒服就用哪個,效果一樣。

define

巨集是一種批量處理的稱謂,簡單來說就是根據定義好的規則替換一定的文字。替換過程在程式編譯期,也因此大量使用巨集會造成編譯時間變長;而且替換過程不進行型別安全檢查;還需要注意“邊緣效應”

引用喵神 【巨集定義的黑魔法】 原文: 巨集定義在C系開發中可以說佔有舉足輕重的作用。底層框架自不必說,為了編譯優化和方便,以及跨平臺能力,巨集被大量使用,可以說底層開發離開define將寸步難行。而在更高層級進行開發時,我們會將更多的重心放在業務邏輯上,似乎對巨集的使用和依賴並不多。但是使用巨集定義的好處是不言自明的,在節省工作量的同時,程式碼可讀性大大增加。如果想成為一個能寫出漂亮優雅程式碼的開發者,巨集定義絕對是必不可少的技能
得益於巨集定義的高效與靈活性, 在很多底層系統中大量被使用, 其玩法也非常的多, 感興趣的可以參考喵神這篇文章

巨集定義的黑魔法 - 巨集菜鳥起飛手冊

define 與 const 選擇

巨集定義是在預編譯期間處理,在使用時系統直接進行的方法替換,靜態變數等則是在編譯期間進行的。巨集定義不會被系統做編譯檢查,所以型別錯誤也能通過編譯,const則會做編譯檢查。能顯式的宣告資料型別,並且不會出現自己定義的巨集被其他人員更換,導致出現難以排查的Bug。巨集不僅能對資料型別進行定義,還能對函式, 結構體,方法等進行定義相對比起常量來說作用會更多一些。

總結

  1. 編譯時刻:巨集是預編譯, const是編譯階段
  2. 編譯檢查:巨集不做檢查, 有錯誤不會提示, const會檢查, 有錯誤會提示
  3. 巨集的優點:高效,靈活,可用於替換各種 函式,方法,結構體,資料等;
  4. 巨集的缺點:由於在預編譯期間完成, 大量使用巨集, 容易造成編譯時間久
  5. const優點:編譯器通常不為普通const常量分配儲存空間,而是將它們儲存在符號表中, 這使得它成為一個編譯期間的常量,沒有了儲存與讀記憶體的操作,使得它的效率也很高, 相當於巨集更加高效, 並且容錯率很低。
  6. const缺點:const 能定義的內容非常有限, 只能用於定義常量
  7. 巨集定義所定義的生命週期與所在的載體的生命週期有關
  8. const修飾具有就近性,即const後面的引數是不可變的,const修飾的引數具有隻讀性, 如果試圖修改, 編譯器就會報錯
  9. 蘋果官方不推薦我們使用巨集, 推薦使用const常量

static與const

static與const同時修飾一個變數時,該變數變為靜態的只讀變數,無法被外部檔案訪問也無法被修改。

通常用於全域性的資料常量或字串常量,這些常量定義之後就不需要也不能修改,且作用域僅在本.m檔案中。

類似下面的栗子,這些常量僅在一個.m檔案使用,且定義之後就不需要也不能修改,就應該使用static與const同時修飾。

static NSString *const titleOfViewController = @"首頁";
 
static NSInteger const PI = 3.1415926;

extern與const

多個檔案中都經常使用的相同的字串常量,就需要使用extern與const同時修飾,可供外部檔案訪問且不可修改。

通常我們會建立一個SUNConst.h和SUNConst.m來統一管理全域性變數(全域性變數遍佈整個專案將維護艱難),此時就要用到extern與const。

SUNConst.h負責定義:

extern NSString *const name;
 
extern NSInteger const PI;

SUNConst.m負責實現:

NSString *const name = @"SunSatan";
 
NSInteger const PI = 3.1415926;

轉自:

iOS-extern、static、const詳解

iOS 常用關鍵字 static、const、 extern、define