iOS 常用關鍵字 static、const、extern、define
static
static對變數的修飾在編譯階段執行,被static修飾的變數在編譯階段會進行編譯檢查,會報編譯錯誤。
被static修飾的變數僅在編譯階段初始化一次,在全域性/靜態區為它分配一份記憶體,一直到程式結束執行由系統回收。
修飾區域性變數
- 延長區域性變數的生命週期(儲存區域從棧移動到靜態區), 程式結束才會銷燬。
- 區域性變數只會生成一份記憶體, 不管方法執行多少次, 其只會初始化一次。
例:在一個類的裡面列印下面的方法,只要程式不銷燬, 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
修飾全域性變數
- 被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 作用
- 將位於const右部的變數修飾為常量
- 被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。巨集不僅能對資料型別進行定義,還能對函式, 結構體,方法等進行定義相對比起常量來說作用會更多一些。
總結
- 編譯時刻:巨集是預編譯, const是編譯階段
- 編譯檢查:巨集不做檢查, 有錯誤不會提示, const會檢查, 有錯誤會提示
- 巨集的優點:高效,靈活,可用於替換各種 函式,方法,結構體,資料等;
- 巨集的缺點:由於在預編譯期間完成, 大量使用巨集, 容易造成編譯時間久
- const優點:編譯器通常不為普通const常量分配儲存空間,而是將它們儲存在符號表中, 這使得它成為一個編譯期間的常量,沒有了儲存與讀記憶體的操作,使得它的效率也很高, 相當於巨集更加高效, 並且容錯率很低。
- const缺點:const 能定義的內容非常有限, 只能用於定義常量
- 巨集定義所定義的生命週期與所在的載體的生命週期有關
- const修飾具有就近性,即const後面的引數是不可變的,const修飾的引數具有隻讀性, 如果試圖修改, 編譯器就會報錯
- 蘋果官方不推薦我們使用巨集, 推薦使用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;
轉自: