iOS中比較經常使用的關鍵字

本文概覽

前言:

咱們看源碼,或者面試常常遇到一些關鍵字,又因爲網上的相關文章部分觀點錯誤,我在此彙總了我以前的筆記以及查閱相關書籍,站在巨人的肩膀上,整合出此篇文章。html

總之,爲了提高,爲了面試,瞭解這些關鍵字,很是有必要。每一個觀點,我儘量的結合代碼講解。面試

extern

當編譯器遇到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

static

一、修飾全局變量
  • 全局變量的做用域僅限於當前文件(限制做用域)
二、修飾局部變量(下面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.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 ,我先給出例子:

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 的配合

一、使用
static const CGFloat Height = 180;
static const CGFloat author = @"Dwyane";
複製代碼
二、與 #define 的對比
  • 共同點:一旦定義,都不容許修改
  • 不一樣點:static const修飾變量只有一分內存,檢查數據類型;#define僅僅簡單文字替換,不會檢查類型,每次使用都須要建立一分內存

inline 內聯函數

在講解內聯函數前,咱們先來看看調用函數是多麼耗時間

#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 比較

建議也看宏與普通函數的區別

inline 函數與 #define 區別: 一、 宏調用並不執行類型檢查,甚至連正常參數也不檢查,可是函數調用卻要檢查。 二、 C語言的宏使用的是文本替換,可能致使沒法預料的後果,由於須要從新計算參數和操做順序。 三、 許多結構體使用宏或者使用不一樣的語法來表達很難理解。內聯函數使用與普通函數相同的語言,能夠隨意的內聯和不內聯。 四、 內聯代碼的調試信息一般比擴展的宏代碼更有用。




Static參考

內聯函數的參考

iOS OC內聯函數 inline

相關文章
相關標籤/搜索