OC中的靜態(static)/ 內聯(inline)函數

原由

項目中用到了YYKit中的一些組件,好比YYTextYYImage,因而抽了點時間閱讀了一下當中的一些代碼。

發現了在YYKit中用了不少的函數方法,好比:函數

static inline UIEdgeInsets UIEdgeInsetRotateVertical(UIEdgeInsets insets) {
     UIEdgeInsets one;
     one.top = insets.left;
     one.left = insets.bottom;
     one.bottom = insets.right;
     one.right = insets.top;
     return one;
}
複製代碼

這種:spa

static void YYTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness) {
    CGFloat maxXHeight = 0;
    CGFloat maxUnderlinePos = 0;
    CGFloat maxLineThickness = 0;
    for (NSUInteger i = 0, max = CFArrayGetCount(runs); i < max; i++) {
        CTRunRef run = CFArrayGetValueAtIndex(runs, i);
        CFDictionaryRef attrs = CTRunGetAttributes(run);
        if (attrs) {
            CTFontRef font = CFDictionaryGetValue(attrs, kCTFontAttributeName);
            if (font) {
                CGFloat xHeight = CTFontGetXHeight(font);
                if (xHeight > maxXHeight) maxXHeight = xHeight;
                CGFloat underlinePos = CTFontGetUnderlinePosition(font);
                if (underlinePos < maxUnderlinePos) maxUnderlinePos = underlinePos;
                CGFloat lineThickness = CTFontGetUnderlineThickness(font);
                if (lineThickness > maxLineThickness) maxLineThickness = lineThickness;
            }
        }
    }
    if (xHeight) *xHeight = maxXHeight;
    if (underlinePosition) *underlinePosition = maxUnderlinePos;
    if (lineThickness) *lineThickness = maxLineThickness;
}
複製代碼

還有這種:.net

CGFloat YYTextScreenScale() {
    static CGFloat scale;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        scale = [UIScreen mainScreen].scale;
    });
    return scale;
}
複製代碼

以上三種函數使用類似、長相相似,可是仍然有些許的不一樣。 咱們觀察一下,可知:調試

一、第一個方法,使用了static和inline標示符,返回值顯示爲UIEdgeInsets類型。
二、第二個方法,只是只用了static,返回值爲void類型。
三、第三個方法,乾脆連static也省掉了,返回值爲CGFloat。
複製代碼

那麼三者到底有什麼區別呢?往下看


分析

上面觀察得出,三者基本上的區別就是標示符使用上的區別,那麼咱們分析下,表示符不一樣使用狀況下,會有什麼優點和好處。code

1、引用inline標示符

引用inline標示符,可以使函數一做爲一個標準的內聯函數,函數的代碼被放入符號表中,在使用時直接進行替換,(像宏同樣展開)。blog

通常狀況下引入內聯函數是爲了解決函數調用效率的問題,函數之間調用,是內存地址之間的調用,當函數調用完畢以後還會返回原來函數執行的地址,會有必定的時間開銷,內聯函數就是爲了解決這一問題。 不用inline修飾的函數,彙編時會調用 call 指令,調用call指令就是就須要:生命週期

1)將下一條指令的所在地址入棧
2)並將子程序的起始地址送入PC(因而CPU的下一條指令就會轉去執行子程序).
複製代碼

GCC中的inline函數能夠至關於在一個普通的全局函數加上inline屬性。inline關鍵字僅僅是建議編譯器在編譯的時候作內聯展開處理,而不是強制在gcc編譯器中,編譯器能夠忽略這個建議的,某一些狀況下編譯器會自動忽略這個inline,將這個函數還原成普通函數。若是編譯選項設置爲負無窮,即便是inline函數也不會被內聯展開,除非設置了強制內聯展開的屬性(attribute((always_inline))),即NS_INLINE這個宏定義。內存

2、引用static標示符

一般狀況下使用是用做聲明靜態變量。作用域

1)修飾局部變量的時候,讓局部變量只初始化一次,局部變量在程序中只有一分內存,可是並不會改變局部變量的做用域,僅僅是改變了局部變量的生命週期(只到程序結束,這個局部變量纔會銷燬)。
2)修飾全局變量的時候,全局變量的做用域僅限於當前文件。
複製代碼

當修飾函數的時候,對函數的鏈接方式產生影響,使得函數只在本文件內部有效,對其餘文件是不可見的。這樣的函數又叫做靜態函數。使用靜態函數的好處是,不須要擔憂在其餘文件存在同名的函數從而產生干擾。get

若是想要其餘文件能夠引用本地函數,則要在函數定義時使用關鍵字extern,表示該函數是外部函數,可供其餘文件調用。另外在要引用別的文件中定義的外部函數的文件中,使用extern聲明要用的外部函數便可。

另外也由於使用了static修飾,從而保證了不會不斷地調用copy,保證了函數地址的一致性,減少了內存壓力。

3、沒有使用以上兩個標識符修飾

簡單瀏覽一下這個函數,咱們能夠發現這個函數是一個單例方法的構造,相對比較少見的用法,相對簡潔,本仍然是一個簡單的函數方法,能夠被外界用CG_EXTERN調用。

用static修飾的函數,本限定在本源碼文件中,不能被本源碼文件之外的代碼文件調用。而普通的函數,默認是extern的,也就是說,能夠被其它代碼文件調用該函數。  


總結

1、爲何inline函數能取代宏?

1)#define定義的函數要有特別的格式要求,並非每一個人都能熟練使用,而使用`inline`則就行日常寫函數那樣。
2)和其餘的宏定義同樣,使用define宏定義的代碼,編譯器不會對其進行參數有效性檢查,很容易出現沒法察覺的錯誤,調試過程當中會出現不少麻煩。
3)不只是輸入類型,#define宏定義的代碼,返回值不能被強制轉換成可轉換的適合的轉換類 。
4)#define是文本替換,須要在預編譯時展開,內聯函數是編譯時候展開。
複製代碼

2、inline函數優勢相比於普通函數:

1)inline函數避免了普通函數的,在彙編時必須調用call的缺點:取消了函數的參數壓棧,減小了調用的開銷,提升效率.因此執行速度確比通常函數的執行速度要快.
2)集成了宏的優勢,使用時直接用代碼替換(像宏同樣);
複製代碼

3、inline內聯函數的說明

1)內聯函數只是咱們向編譯器提供的申請,編譯器不必定採起inline形式調用函數。
2)內聯函數不能承載大量的代碼,若是內聯函數的函數體過大,編譯器會自動放棄內聯。
3)內聯函數內不容許使用循環語句或開關語句。
4)內聯函數的定義須在調用以前。
5)當使用內聯函數時,若是在多處調用了此內聯函數,則此函數就會有N次代碼段的拷貝,因此多配合`static`標示符使用。
複製代碼

Refrence

另外

相關文章
相關標籤/搜索