【轉】ios內聯函數 inline

ios內聯函數 inline

原因

因爲在學習使用UIScrollVew開發的過程當中,碰到下面這個屬性(設置內邊距):ios

@property(nonatomic) UIEdgeInsets scrollIndicatorInsets; // default is UIEdgeInsetsZero. adjust indicators inside 
  • 1

光看UIEdgeInsets這個類型,一時還不知道它的具體內部結構是怎麼樣的,因而繼續點進去發現它的定義以下:面試

typedef struct UIEdgeInsets { CGFloat top, left, bottom, right; // specify amount to inset (positive) for each of the edges. values can be negative to 'outset' } UIEdgeInsets;
  • 1
  • 2
  • 3

原來是這樣一個結構體!~ 隨之,看到和UIEdgeInsets相關的使用方法,列舉部分:xcode

UIKIT_STATIC_INLINE UIEdgeInsets UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right) {
    UIEdgeInsets insets = {top, left, bottom, right};
    return insets; } UIKIT_STATIC_INLINE UIOffset UIOffsetMake(CGFloat horizontal, CGFloat vertical) { UIOffset offset = {horizontal, vertical}; return offset; } ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

看着上面的代碼,又出現了一個我不認識的東西UIKIT_STATIC_INLINE,繼續點進去查看發現這是這麼一個宏:ide

#define UIKIT_STATIC_INLINE static inline
  • 1

哦,原來它的意思是告訴編譯器這個函數是一個靜態的內聯函數!內聯函數?好耳熟啊,可是想不起來具體有什麼做用了,因而百度百度!!得出的能令我印象深入的結論是:函數

  • 引入內聯函數是爲了解決函數調用效率的問題學習

  • 因爲函數之間的調用,會從一個內存地址調到另一個內存地址,當函數調用完畢以後還會返回原來函數執行的地址。函數調用會有必定的時間開銷,引入內聯函數就是爲了解決這一問題。測試

實踐

那麼引用內聯函數到底有什麼區別呢?萬一面試問到了,那隻能回答」爲了解決函數調用效率的問題」?若是面試官再問「如何解決呢?」,那豈不是歇菜了!!不如本身寫代碼測試看看?!!打開xcode..ui

代碼一

說明:定義一個add(int,int)函數並聲明爲static inline,並調用。atom

頭文件:inline.hurl

// inline.h // inline // Created by fenglh on 15/8/24. // Copyright (c) 2015年 fenglh. All rights reserved. #ifndef inline_inline_h #define inline_inline_h static inline int add(int a, int b){ return a+b; } #endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

.m文件:main.m

// main.m // inline // Created by fenglj on 15/8/24. // Copyright (c) 2015年 fenglh. All rights reserved. #import <Foundation/Foundation.h> #import "inline.h" int main(int argc, const char * argv[]) { int c = add(1, 2); return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

查看main.m的彙編文件,以下:

.section __TEXT,__text,regular,pure_instructions .globl _main .align 4, 0x90 _main: ## @main Lfunc_begin0: .loc 2 14 0 ## /Users/fenglihai/Desktop/inline/inline/main.m:14:0 .cfi_startproc ## BB#0: pushq %rbp Ltmp0: .cfi_def_cfa_offset 16 Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbp Ltmp2: .cfi_def_cfa_register %rbp ##DEBUG_VALUE: main:argc <- EDI ##DEBUG_VALUE: main:argv <- RSI Ltmp3: ##DEBUG_VALUE: main:c <- 3 xorl %eax, %eax .loc 2 17 5 prologue_end ## /Users/fenglihai/Desktop/inline/inline/main.m:17:5 Ltmp4: popq %rbp retq
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

代碼二

說明:定義一個add(int,int)函數並調用。

頭文件:Header.h

// Header.h // notInline // Created by fenglh on 15/8/25. // Copyright (c) 2015年 fenglh. All rights reserved. #ifndef notInline_Header_h #define notInline_Header_h int add(int a, int b){ return a+b; } #endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

.m文件:main.m

// main.m // notInline // Created by fenglh on 15/8/25. // Copyright (c) 2015年 fenglh. All rights reserved. // #import <Foundation/Foundation.h> #import "Header.h" int main(int argc, const char * argv[]) { int c = add(1,2); return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

查看main.m的彙編文件,以下:

.section    __TEXT,__text,regular,pure_instructions
    .globl  _add
    .align  4, 0x90 _add: ## @add Lfunc_begin0: .file 3 "/Users/fenglihai/Desktop/notInline/notInline" "Header.h" .loc 3 12 0 ## /Users/fenglihai/Desktop/notInline/notInline/Header.h:12:0 .cfi_startproc ## BB#0: pushq %rbp Ltmp0: .cfi_def_cfa_offset 16 Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbp Ltmp2: .cfi_def_cfa_register %rbp movl %edi, -4(%rbp) movl %esi, -8(%rbp) .loc 3 13 5 prologue_end ## /Users/fenglihai/Desktop/notInline/notInline/Header.h:13:5 Ltmp3: movl -4(%rbp), %esi addl -8(%rbp), %esi movl %esi, %eax popq %rbp retq Ltmp4: Lfunc_end0: .cfi_endproc .globl _main .align 4, 0x90 _main: ## @main Lfunc_begin1: .loc 2 12 0 ## /Users/fenglihai/Desktop/notInline/notInline/main.m:12:0 .cfi_startproc ## BB#0: pushq %rbp Ltmp5: .cfi_def_cfa_offset 16 Ltmp6: .cfi_offset %rbp, -16 movq %rsp, %rbp Ltmp7: .cfi_def_cfa_register %rbp subq $32, %rsp movl $1, %eax movl $2, %ecx movl $0, -4(%rbp) movl %edi, -8(%rbp) movq %rsi, -16(%rbp) .loc 2 13 13 prologue_end ## /Users/fenglihai/Desktop/notInline/notInline/main.m:13:13 Ltmp8: movl %eax, %edi movl %ecx, %esi callq _add xorl %ecx, %ecx movl %eax, -20(%rbp) .loc 2 14 5 ## /Users/fenglihai/Desktop/notInline/notInline/main.m:14:5 movl %ecx, %eax addq $32, %rsp popq %rbp retq
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

上面2段代碼能夠看出,只有add函數的定義不同,一個是加了static inline修飾,而另一個沒有。再對比一下彙編代碼,發現的確有很大的不同呀!!!給人第一感受,有用static inline修飾的彙編以後的代碼比沒有static inline修飾的的彙編以後的代碼簡潔的多了!!

其次,在沒有調用static inline修飾add函數的main.m彙編代碼中,add函數是有單獨的彙編代碼的! 
使用內聯函數的彙編代碼

而沒有使用內聯函數的main.m彙編代碼中,僅僅只有main函數的彙編代碼! 
沒有使用內聯函數的彙編代碼

再看看使用了內聯函數的main.m彙編代碼: 
這裏寫圖片描述

對比二者的mian.m的彙編代碼,能夠發現,沒有使用`static inline修飾的內聯函數的mian函數彙編代碼中,會出現 call 指令!這就是區別!調用call指令就是就須要: 
(1)將下一條指令的所在地址(即當時程序計數器PC的內容)入棧 
(2)並將子程序的起始地址送入PC(因而CPU的下一條指令就會轉去執行子程序)。

恩恩,對於彙編就不扯淡了,憑藉着上學時候學過點彙編只能深刻到這裏了!唉!那麼得知,影響效率的緣由就是在解決在call調用這裏了!!

結論

1.使用inline修飾的函數,在編譯的時候,會把代碼直接嵌入調用代碼中。就至關於用#define 宏定義來定義一個add 函數那樣!與#define的區別是: 
1)#define定義的格式要有要求,而使用inline則就行日常寫函數那樣,只要加上`inline便可! 
2)使用#define宏定義的代碼,編譯器不會對其進行參數有效性檢查,僅僅只是對符號表進行替換。 
3)#define宏定義的代碼,其返回值不能被強制轉換成可轉換的適合的轉換類型。可參考百度文科 關於inline

2.在inline加上`static修飾符,只是爲了代表該函數只在該文件中可見!也就是說,在同一個工程中,就算在其餘文件中也出現同名、同參數的函數也不會引發函數重複定義的錯誤!**

實踐到這裏,對於內聯函數終的理解,終於加深理解和記憶了!!

相關文章
相關標籤/搜索