咱們看源碼,或者面試常常遇到一些關鍵字,又因爲網上的相關文章部分觀點錯誤,我在此彙總了我以前的筆記以及查閱相關書籍,站在巨人的肩膀上,整合出此篇文章。html
總之,爲了提高,爲了面試,瞭解這些關鍵字,很是有必要。每一個觀點,我儘量的結合代碼講解。面試
當編譯器遇到extern
模板聲明時,它不會在本文件中生成實例化代碼,將一個實例化聲明爲extern
就表示開發者承諾在程序的其餘位置有該實例化的一個非extern
定義。對於一個給定的實例化版本,可能有多個extern
聲明,但必須只有一個定義。緩存
一、在其餘文件(DWConst
)的實現文件聲明變量bash
// DWConst.m
// 定義了整個程序都能訪問的常量
const NSString *myExtern = @"abc";
@implementation DWConst
@end
複製代碼
二、在 ViewController
類賦值並打印(不用導入DWConst.h
)函數
// ViewController.m
extern NSString *myExtern;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"myExtern=%@", myExtern);
NSLog(@"myExtern 地址=%p", &myExtern);
myExtern = @"hello";
NSLog(@"myExtern=%@", myExtern);
NSLog(@"從新賦值後的myExtern 地址=%p", &myExtern);
}
複製代碼
打印: myExtern=abc myExtern 地址=0x108aaf180 myExtern=hello 從新賦值後的myExtern 地址=0x108aaf180ui
定義後,不管後面怎樣使用,都只是共用一個內存地址(看上面打印),也代表任意一處改變值,都會影響其餘處。spa
- (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.net
由此看出,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 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 CGFloat Height = 180;
static const CGFloat author = @"Dwyane";
複製代碼
#define
的對比static const
修飾變量只有一分內存,檢查數據類型;#define
僅僅簡單文字替換,不會檢查類型,每次使用都須要建立一分內存在講解內聯函數前,咱們先來看看調用函數是多麼耗時間
#include <stdio.h>
#include <time.h>
float addTime(long int a, float b) {
float result;
result = a + b;
return result;
}
int main() {
long int i;
float result = 0;
time_t start_time, stop_time;
time(&start_time);
printf("開始計算循環時間...\n");
for(i = 1; i <= 1000000000; i++)
{
result += i;
}
time(&stop_time);
printf("總消耗時間:%ld\n",stop_time-start_time);
printf("result = %f\n", result);
result = 0;
time(&start_time);
printf("開始計算調用函數時間...\n");
for(i = 1; i <= 1000000000; i++)
{
result = addTime(i, result); //使用函數調用方式,消耗時間
}
printf("result = %f\n", result);
time(&stop_time);
printf("總消耗時間:%ld\n",stop_time-start_time);
return 0;
}
複製代碼
//打印結果
開始計算循環時間...
總消耗時間:3
result = 18014398509481984.000000
開始計算調用函數時間...
result = 18014398509481984.000000
總消耗時間:6
Program ended with exit code: 0
複製代碼
上述代碼,一個是自加方式,一個是調用函數相加方式,都是作同一件事(求累積和),求出結果相同,可是調用函數相加方式耗費時間卻遠超自加方式。這個栗子充分的表示,調用函數很是耗時,可是不少時候咱們不得不用函數,爲了儘量的提高效率,內聯函數來了~
static inline int DWMax(int x, int y) {
return (x > y)? x : y;
}
- (void)viewDidLoad {
int i = DWMax(20, 30);
NSLog(@"max = %d", i);
}
複製代碼
對於一些函數體代碼不是很大,但又頻繁地被調用的函數來說,解決其效率問題更爲重要。引入內聯函數實際上就是爲了解決這一問題。
濫用內聯將致使程序變慢. 內聯可能使目標代碼量或增或減, 這取決於內聯函數的大小. 內聯很是短小的存取函數一般會減小代碼大小, 但內聯一個至關大的函數將戲劇性的增長代碼大小. 現代處理器因爲更好的利用了指令緩存, 小巧的代碼每每執行更快。
注意:內聯函數只是咱們向編譯器提供的申請,編譯器不必定採起inline形式調用函數.另外,若是不申請,編譯器會選擇性的自動會彙編成內聯函數
結論: 一個較爲合理的經驗準則是, 不要內聯超過 10 行的函數. 謹慎對待析構函數, 析構函數每每比其表面看起來要更長, 由於有隱含的成員和基類析構函數被調用! 另外一個實用的經驗準則: 內聯那些包含循環或 switch 語句的函數經常是得不償失 (除非在大多數狀況下, 這些循環或 switch 語句從不被執行). -->參考
建議也看宏與普通函數的區別
inline
函數與#define
區別: 一、 宏調用並不執行類型檢查,甚至連正常參數也不檢查,可是函數調用卻要檢查。 二、 C語言的宏使用的是文本替換,可能致使沒法預料的後果,由於須要從新計算參數和操做順序。 三、 許多結構體使用宏或者使用不一樣的語法來表達很難理解。內聯函數使用與普通函數相同的語言,能夠隨意的內聯和不內聯。 四、 內聯代碼的調試信息一般比擴展的宏代碼更有用。