iOS不可錯過的關鍵字
建議檢視原文: ofollow,noindex">https://www.jianshu.com/p/dce05b24d288 (不定時更新)

本文概覽
前言:我們看原始碼,或者面試經常遇到一些關鍵字,又由於網上的相關文章部分觀點錯誤,我在此彙總了我之前的筆記以及查閱相關書籍,站在巨人的肩膀上,整合出此篇文章。
總之,為了提升,為了面試,瞭解這些關鍵字,非常有必要。每個觀點,我儘可能的結合程式碼講解。
extern
當編譯器遇到 extern
模板宣告時,它不會在本檔案中生成例項化程式碼,將一個例項化宣告為 extern
就表示承諾在程式的其他位置有該例項化的一個非 extern
定義。對於一個給定的例項化版本,可能有多個 extern
宣告,但必須只有一個定義。
1、在其他檔案( DWConst
)的實現檔案宣告變數
// DWConst.m // 定義了整個程式都能訪問的常量 const NSString *myExtern = @"abc"; @implementation DWConst @end
2、在 ViewController
類賦值並列印(不用匯入 DWConst.h
)
// ViewController.m NSLog(@"myExtern=%@", myExtern); NSLog(@"myExtern 地址=%p", &myExtern); myExtern = @"hello"; NSLog(@"myExtern=%@", myExtern); NSLog(@"重新賦值後的myExtern 地址=%p", &myExtern);
列印:
myExtern=abc
myExtern 地址=0x108aaf180
myExtern=hello
重新賦值後的myExtern 地址=0x108aaf180
定義後,無論後面怎樣使用,都只是共用一個記憶體地址(看上面列印),也表明任意一處改變值,都會影響其他處。
static
1、修飾全域性變數
- 全域性變數的作用域僅限於當前檔案(限制作用域)
2、修飾區域性變數(下面3個作用,自我覺得,實質是一樣的)
- 保證只會開闢一個記憶體
- 只會初始化一次
- 沒有改變區域性變數的作用域,僅僅是改變了區域性變數的生命週期(直到程式結束,這個區域性變數才會銷燬)
- (void)viewDidLoad { NSLog(@"----------------- ViewDidLoad -----------------"); [self testStatic]; } // 已省略按鈕建立程式碼 - (void)btnClicked { NSLog(@"----------------- btnClicked -----------------"); [self testStatic]; } - (void)testStatic { NSInteger i = 0; i++; static NSInteger staticValue = 0; staticValue++; NSLog(@"i的地址=%p,staticValue的地址=%p", &i, &staticValue); }
列印:
----------------- ViewDidLoad -----------------
i的地址=0x7ffee2200948,staticValue的地址=0x10d9ff1c8
----------------- btnClicked -----------------
i的地址=0x7ffee2200fd8,staticValue的地址=0x10d9ff1c8
由此看出,staticValue 的地址沒有變,證明了 ”保證只會開闢一個記憶體“
把 NSLog(@"i的地址=%p,staticValue的地址=%p", &i, &staticValue);
替換成
NSLog(@"i = %ld, s.value = %ld", (long)i, (long)staticValue);
列印:
----------------- ViewDidLoad -----------------
i = 1, s.value = 1
----------------- btnClicked -----------------
i = 1, s.value = 2
由此看出,staticValue 的有 +1,而 i 永遠都是 1,證明了 static 關鍵字修飾區域性變數”延長了宣告週期“,因為 i 沒修飾,則每次都重新建立,再 +1
const
至於 const
,我先給出例子:
const int *p = NULL; *p = 20; // 報錯
*p 會報錯 ,被 const 修飾的 *p 只能被賦值一次, 再次賦值,就會報錯 “Read-only variable is not assignable”
int a = 10; p = &a; // 正常通過 NSLog(@"*p=%d", *p); NSLog(@"p=%p", p);
列印:
*p=10
p=0x7ffeec2a9a04
int * const p1 = NULL; NSLog(@"p1=%p", p1); *p1 = 30; // 編譯通過,但執行報錯
看清楚!p 還是可以修改的,不同於 *p 。因為 const 修飾的是 *p ,而不是 p
*p 和 p 的區別:
*p 是儲存的值,而 p 是指標。上例中,p 指標,則儲存著 a 的記憶體地址,而 *p 等於10。
同樣,在 id 型別的運用
NSString * const city = @"CN"; city = @"US"; // 報錯,
const 修飾的 city 為只讀屬性
static 和 const 的配合
1、使用
static const CGFloat Height = 180; static const CGFloat author = @"Dwyane";
2、與 #define
的對比
- 共同點:一旦定義,都不允許修改
- 不同點:
static const
修飾變數只有一份記憶體,檢查資料型別;#define
僅僅簡單文字替換,不會檢查型別,每次使用都需要建立一份記憶體
inline 行內函數
1、使用
static inline int DWMax(int x, int y) { return (x > y)? x : y; } - (void)viewDidLoad { int i = DWMax(20, 30); NSLog(@"max = %d", i); }
2、引入行內函數的目的
對於一些函式體程式碼不是很大,但又頻繁地被呼叫的函式來講,解決其效率問題更為重要。引入行內函數實際上就是為了解決這一問題。
3、濫用行內函數的弊端
濫用內聯將導致程式變慢. 內聯可能使目的碼量或增或減, 這取決於行內函數的大小. 內聯非常短小的存取函式通常會減少程式碼大小, 但內聯一個相當大的函式將戲劇性的增加程式碼大小. 現代處理器由於更好的利用了指令快取, 小巧的程式碼往往執行更快。
注意:行內函數只是我們向編譯器提供的申請,編譯器不一定採取inline形式呼叫函式.另外,如果不申請,編譯器會選擇性的自動會彙編成行內函數
結論: 一個較為合理的經驗準則是, 不要內聯超過 10 行的函式. 謹慎對待解構函式, 解構函式往往比其表面看起來要更長, 因為有隱含的成員和基類解構函式被呼叫!
另一個實用的經驗準則: 內聯那些包含迴圈或 switch 語句的函式常常是得不償失 (除非在大多數情況下, 這些迴圈或 switch 語句從不被執行). --> 參考
inline 函式與 #define 比較
建議也看 巨集與普通函式的區別
inline
函式與 #define
區別:
1、 巨集呼叫並不執行 型別檢查 ,甚至連正常引數也不檢查,但是函式呼叫卻要檢查。
2、 C語言的巨集使用的是文字替換,可能導致無法預料的後果,因為需要重新計算引數和 操作順序 。
3、 許多結構體使用巨集或者使用不同的語法來表達很難理解。行內函數使用與普通函式相同的語言,可以隨意的內聯和不內聯。
4、 內聯程式碼的除錯資訊通常比擴充套件的巨集程式碼更有用。