static、const、extern的正確使用方式

窺探iOS底層實現--OC對象的本質(一) - 掘金objective-c

窺探iOS底層實現--OC對象的本質(二) - 掘金swift

窺探iOS底層實現--OC對象的分類:instance、class、meta-calss對象的isa和superclass - 掘金安全

窺探iOS底層實現-- KVO/KVC的本質 - 掘金函數

1.extern

全局變量extern,也稱之爲外部變量,是在方法外部定義的變量。它不屬於那個方法,而是屬於整個源程序。做用因而整個程序。若是全局變量和局部變量重名,則在局部變量做用域內,全局變量被屏蔽,不起做用post

///> DDExtern.h
    
    #import <Foundation/Foundation.h>
    NSString *flag = @"DDExtension";
    @interface DDExtern : NSObject
    @end
複製代碼

定義了一個字符串 flagui

///> main.m 

#import <Foundation/Foundation.h> 
int main(int argc, const char \* argv\[\]) { 
    extern NSString *flag; 
    NSLog(@"%@",flag); 
    return 0; 
}
複製代碼
打印結果:DDExtension
複製代碼

從例子中能夠看出,main.m無需導入JJExtern的頭文件,直接在NSString *flag前面加上extern關鍵字就能夠取到JJExtern的flag值。 須要注意的是,extern修飾的變量名必須是和Extern下的變量名一致,即都爲flag,不然會都提示找不到。 還須要注意extern修飾的變量是沒有真的內存的。spa

  • 總結:

    • 想要訪問全局變量,能夠在變量前加一個extern
    • extern修飾的變量沒有真正的內存

問題:

既然只須要extern就能獲得並修改其餘文件的變量,這樣不是很不安全?由於隨時都會被人改掉,怎麼辦??3d

答案:

使用接下來所講的 static關鍵字修飾變量,那麼該變量就只能在本文件中修改,其餘文件沒法使用extern獲取變量指針

2.static

static 既能夠修飾全局變量,又能夠修飾局部變量。code

  • 修飾全局變量

    使用以前的例子,在JJExtern的 NSString *flag = @"JJExtension"; 前面加 static,以下

    ///> JJExtern.h
    
      #import <Foundation/Foundation.h>
      static NSString *flag = @"DDExtension";
      @interface JJExtern : NSObject
      @end
    複製代碼

    再次運行main函數 編譯會報錯

    note_staticAbout_01.png
    因此只要在全局變量前加static,那麼這個全局變量的做用域就會縮小到當前文件,外界就不能訪問了

    • 總結:

      • static修飾全局變量,保證全局變量安全,外界不能夠訪問與修改,做用域僅限於當前文件。
  • 修飾局部變量:

代碼:

///> main.m
void test() {
    static int a = 0; 
    a++; 
    NSLog(@"a = %d", a); 
} 
int main(int argc, const char \* argv\[\]) { 
    @autoreleasepool { 
      for (int i = 0; i<3; i++) { 
          test(); 
      }
    } 
    return 0;
}

/** 輸出結果: * 2018-12-05 19:20:55.494405+0800 tesy[11959:2261816] a = 1 * 2018-12-05 19:20:55.499893+0800 tesy[11959:2261816] a = 2 * 2018-12-05 19:20:55.505727+0800 tesy[11959:2261816] a = 3 */

複製代碼

修飾局部變量時,做用域僅限於test函數的大括號內,其餘地方都不可使用。test這個函數中若是不添加 static,那麼a打印出來永遠都是1,由於在運行完此段函數 局部變量a就會被釋放。從新執行函數時a++爲0+1.

加上static以後的含義就改變了,結果爲1,2,3。由於被static修飾的變量只會初始化一次,永遠都只有一分內存,因此當第一次調用test函數時a就已經被初始化了,a有一個內存空間而且值爲0,第二次調用test函數因爲a被static修飾,因此不會再初始化新的值,它會拿到以前的那分內存進行a++操做,就會變成1,以此類推。

  • 總結:

    • 讓局部變量只初始化一次
    • 局部變量在內存中只有一分內存
    • 直到程序結束纔會被銷燬

3. const

const的做用和宏相似,蘋果不推薦使用宏定義,推薦使用const,因此在swift中蘋果拋棄了宏的使用。

  • const 和 宏的區別

    \ const
    編譯時刻 預編譯(在編譯前處理) 編譯階段
    編譯檢查 不作檢查,不會報編譯錯誤,單純替換功能,用宏定義的函數會報參數類型錯誤 會作編譯檢查,會報編譯錯誤
    宏的好處 宏能定義一些函數、方法 例如RGB函數 不能
    宏的壞處 使用大量的宏,容易形成編譯時間久每次都須要從新替換 1.0.2 -> 1.0.4
    • 使用過多的宏會消耗大量編譯時間。 const的做用就是使右邊的變量,只可讀,不可修改

三段代碼理解const

int x = 1; 

int y = 2; 

const int *px = &x; // 讓指針px指向變量x(此時const右邊是*p) 

px = &y;// 改變指針px的指向,使其指向變量y 

*px = 3; // 改變px指向的變量x的值,出錯:Read-only variable is not assignable

複製代碼
int x = 1; 

int y = 2;

const int *px = &x; // 讓指針px指向變量x(此時const右邊是*p)

px = &y; // 改變指針px的指向,使其指向變量y 

*px = 3; // 改變px指向的變量x的值,出錯:Read-only variable is not assignable

複製代碼
int x = 1; 

int y = 2; 

const int *px = &x;// 讓指針px指向變量x(此時const右邊是*p) 

px = &y; // 改變指針px的指向,使其指向變量y 

*px = 3; // 改變px指向的變量x的值,出錯:Read-only variable is not assignable

複製代碼

上面的三段代碼處理的是基本數據類型,咱們知道OC語言是C語言的超集,因此上面部分基本數據類型的處理OC與C同樣。 可是咱們知道OC是C語言的超集,OC中還有NSString等的數據類型,它們的本質是個結構體,因此在處理指針方面與基本數據類型不一樣

代碼以下:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {   

    NSString const *name = @"milo";// const修飾*name 
    NSLog(@"%@",name);// 打印結果「milo」 
    
    name = @"vicky";// 在oc中NSString等類的值不是經過*name訪問的,而是經過name訪問的,這就是和c語言的指針的區別,但仍是遵循const右邊是誰,誰就只可讀的原則。
    NSLog(@"%@",name);// 打印結果「vicky」 
}

複製代碼

objective-c語言代碼

- (void)viewDidLoad {
  [super viewDidLoad]; 
  // 定義變量 
  int a = 1; 
  // 容許修改值 
  a = 20;
  // const兩種用法 
  // const:修飾基本變量p
  // 這兩種寫法是同樣的,const只修飾右邊的基本變量b 
  const int b = 20; // b:只讀變量 
  int const b = 20; // b:只讀變量 
  b = 1; // 不容許修改值 
  
  
  // const:修飾指針變量\*p,帶\*的變量,就是指針變量. 
  // 定義一個指向int類型的指針變量,指向a的地址 
  int *p = &a; 
  int c = 10; 
  p = &c; 
  // 容許修改p指向的地址,
  // 容許修改p訪問內存空間的值 
  *p = 20;
  
  
  // const修飾指針變量訪問的內存空間,修飾的是右邊*p1, 
  // 兩種方式同樣 
  const int *p1; // *p1:常量 p1:變量 
  int const *p1; // *p1:常量 p1:變量 
  
  // const修飾指針變量p1 
  int * const p1; // *p1:變量 p1:常量
  
  
  // 第一個const修飾*p1 第二個const修飾 p1 
  // 兩種方式同樣 
  const int * const p1; // *p1:常量 p1:常量
  int const * const p1; // *p1:常量 p1:常量 
}
複製代碼
  • 總結:

    • const僅僅用來修飾右邊的變量(基本數據變量px 指針變量*px)
    • 被const修飾的變量是隻讀的

4. static、const結合使用

  • static與const做用:聲明一個只讀的靜態變量
  • 開發使用場景:在一個文件中常用的字符串常量,可使用static與const組合
///> 開發中經常使用static修飾全局變量,只改變做用域
    
///> 爲何要改變全局變量做用域,防止重複聲明全局變量。
    
///> 開發中聲明的全局變量,有些不但願外界改動,只容許讀取。
    
///> 好比一個基本數據類型不但願別人改動
    
///> 聲明一個靜態的全局只讀常量
static const int a = 20;

///> staic和const聯合的做用:聲明一個靜態的全局只讀常量
    
///> iOS中staic和const經常使用使用場景,是用來代替宏,把一個常用的字符串常量,定義成靜態全局只讀變量.

///> 開發中常常拿到key修改值,所以用const修飾key,表示key只讀,不容許修改。
static  NSString * const key = @"name";
    
///> 若是 const修飾 *key1,表示*key1只讀,key1仍是能改變。 
static  NSString const *key1 = @"name";
複製代碼

5. extern與const聯合使用

  • 開發中使用場景:在多個文件中常用的同一個字符串常量,可使用extern與const組合。
  • 緣由:
    • static與const組合:在每一個文件都須要定義一份靜態全局變量。
    • extern與const組合:只須要定義一份全局變量,多個文件共享。
  • 全局常量正規寫法:開發中便於管理全部的全局變量,一般搞一個GlobeConst文件,裏面專門定義全局變量,統一管理,要否則項目文件多很差找。
///> In the header file

extern NSString *const EOCStringConstant;



///> In the implementation file
NSString *const EOCStringConstant = @"VALUE";

複製代碼
相關文章
相關標籤/搜索