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右邊的*q
,3
修飾的是變數q
,切記const
修飾的是右邊的 - 首先下面的
q
都被修飾,也就是q
不能被賦值,然後* const q
又被const
修飾
-
-
- const 僅僅用來修飾 右邊的變數 (基本資料變數p,指標變數
-
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 = 1a的記憶體地址=0x10e758160 a = 2a的記憶體地址=0x10e758160 a = 3a的記憶體地址=0x10e758160 a = 4a的記憶體地址=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_EXTERNextern "C" __attribute__((visibility ("default"))) #else #define UIKIT_EXTERNextern __attribute__((visibility ("default"))) #endif
-
我們只需要把
UIKIT_EXTERN
改為JKKIT_EXTERN
,那以後我們就可以使用我們自己定義的#ifdef __cplusplus #define JKKIT_EXTERNextern "C" __attribute__((visibility ("default"))) #else #define JKKIT_EXTERNextern __attribute__((visibility ("default"))) #endif
-
蘋果和我們自己定義的使用如下
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #ifdef __cplusplus #define JKKIT_EXTERNextern "C" __attribute__((visibility ("default"))) #else #define JKKIT_EXTERNextern __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
-