1. 程式人生 > >趣談iOS執行時之方法呼叫原理

趣談iOS執行時之方法呼叫原理

導語

一個成熟的計算機語言必然有豐富的體系,複雜的容錯機制,處理邏輯以及判斷邏輯。但這些複雜的邏輯都是圍繞一個主線豐富和展開的,所以在學習計算機語言的時候,先掌握核心,然後瞭解其原理,明白程式語言設計的實質和當時選擇這種處理方式的原因是極其必要的,而且也是學習語言的捷徑。

所以在學習的過程中,需要把握幾個核心
先專注主線,后豐富周邊;
先巨集觀瞭解,後微觀精通;
多設身處地思考,理解程式碼設計的原因;
理解程式碼設計的原理和優化

OC中處理方法的業務邏輯和其他語言不同,OC語言是動態語言(動態繫結

動態載入(dynamatic binding),動態型別)。其中動態載入就涉及到OC的執行時。在OC中,方法是動態實現的,呼叫方法實際就是在傳送訊息
試想一下,一個方法的實現必然包含三個部分:

1.執行方法的物件
2.方法名稱
3.不確定的引數

SEL只是一個方法名稱IMP才是執行方法最終的函式,IMP包含這三個部分IMP 是一個函式指標,這個被指向的函式包含一個接收訊息的 物件 id(self 指標), 呼叫方法的選標 SEL (方法名),以及不定個數的方法引數,並返回一個 id。也就是說 IMP 是訊息最終呼叫的執行程式碼,是方法真正的實現程式碼 。

提問時間到了:

  • 動態和靜態有什麼區別?
  • 執行方法是怎麼實現的?
  • OC的方法和C語言的函式原理一樣麼?

動態和靜態有區別的;首先我們從最表層理解,一個方法的實現必然要包含執行者,方法名和不確定的引數和返回值。無論是靜態或者動態方法都必須這三個必要元素(動態和靜態的區別就在於在何時確定這些必要元素)。
方法的執行包含編譯和執行兩個過程。

  • 靜態方法是在編譯時已經確定了三個要素,且不能更改。若型別不對,就會直接發出警告。
  • 而OC的動態方法可以直接跳過編譯,在執行時才開始新增函式呼叫,決定執行方法的三個要素。這就是動態方法(至於怎麼執行,下面開始講解)

這三個元素是如何確定的呢?首先我們看一段示例程式碼

Dog *aDog = [[Dog alloc]init];
[aDog run];

在執行方法時,是怎麼確定的呢?
此時我們需要注意OC內部方法的實質:OC中,方法實現實質就是傳送訊息。
[aDog run];程式碼的實質就是[ objc_sendMsg],它會找到執行方法的三個要素,找到就按照規則執行。
傳送訊息是通過 objc_send(id, SEL, ...) 來實現的,它首先會在物件的類物件的 cachemethodlist 以及父類物件的 cache,methodlist 中依次查詢 SEL 對應 的 IMP;
如果沒有找到且實現了動態方法決議機制就會進行決議,

如果沒有實現動態方法決議機制或決議失敗且實現了訊息轉發機制就會進入訊息轉發流程,否則程式 crash。也就是說如果同時提供了動態方法決議訊息轉發,那麼動態方法決議先於訊息轉發,只有當動態方法決議依然無法正確決議 selector 的 實現,才會嘗試進行訊息轉發。當然,實際過程不可能那麼簡單,在開發語言之初,肯定會完善各種複雜場景和做了很多優化,接下來我們一起研究下OC對方法執行和擴充套件和優化:

  • 第一步:先找方法
  • 第二步:動態方法決議
  • 第三部:訊息轉發
  • 最後: 報錯

訊息轉發

通常,給一個物件傳送它不能處理的訊息會得到出錯提示,然而,Objective-C執行時系統在丟擲錯誤之前, 會給訊息接收物件傳送一條特別的訊息 forwardInvocation 來通該物件,該訊息的唯一引數是個 NSInvocation 型別的物件——該物件封裝了原始的訊息和訊息的引數。我們可以實現 forwardInvocation:方法來對不能處理的訊息做一些預設的處理,也可以將訊息轉發給其他對 象來處理,而不丟擲錯誤。

  • 1,首先去該類的方法cache中查詢,如果找到了就返回它;
  • 2,如果沒有找到,就去該類的方法列表中查詢。如果在該類的方法列表中找到了,則將 IMP 返回,並將 它加入 cache 中快取起來。根據最近使用原則,這個方法再次呼叫的可能性很大,快取起來可以節省下次 呼叫再次查詢的開銷。
  • 3,如果在該類的方法列表中沒找到對應的IMP,在通過該類結構中的super_class指標在其父類結構的方法列表中去查詢,直到在某個父類的方法列表中找到對應的IMP,返回它,並加入cache中;
  • 4,如果在自身以及所有父類的方法列表中都沒有找到對應的 IMP,則看是不是可以進行動態方法決議(後 面有專文講述這個話題);
  • 5,如果動態方法決議沒能解決問題,進入下面要講的訊息轉發流程。便利函式:我們可以通過 NSObject 的一些方法獲取執行時資訊或動態執行一些訊息:
class 返回物件的類;
isKindOfClass,isMemberOfClass 檢查物件是否在指定的類繼承體系中;
respondsToSelector 檢查物件能否相應指定的訊息;
conformsToProtocol 檢查物件是否實現了指定協議類的方法;
methodForSelector 返回指定方法實現的地址;
performSelector:withObject 執行 SEL 所指代的方法

-
OC做為一門面向物件語言,自然具有面向物件的語言特性,如封裝繼承多型。他具有靜態語言的特性(如C++),又有動態語言的效率(動態繫結、動態載入等)。整體來說,確實是一門不錯的程式語言。

OC的動態語言特性

現在,讓我來想想OC的動態語言特性。OC的動態特性表現為了三個方面:
動態型別動態繫結動態載入
之所以叫做動態,是因為必須到執行時(runtime)才會做一些事情。

(1)動態型別

動態型別,說簡單點就是id型別。動態型別是跟靜態型別相對的。像內建的明確的基本型別都屬於靜態型別(int、NSString等)。靜態型別在編譯的時候就能被識別出來。所以,若程式發生了型別不對應,編譯器就會發出警告。而動態型別就編譯器編譯的時候是不能被識別的,要等到執行時(runtime),即程式執行的時候才會根據語境來識別。所以這裡面就有兩個概念要分清:編譯時跟執行時。

(2)動態繫結

動態繫結(dynamic binding)貌似比較難記憶,但事實上很簡單,只需記住關鍵詞@selector/SEL即可。先來看看“函式”,對於其他一些靜態語言,比如c++,一般在編譯的時候就已經將將要呼叫的函式的函式簽名都告訴編譯器了。靜態的,不能改變。而在OC中,其實是沒有函式的概念的,我們叫“訊息機制”,所謂的函式呼叫就是給物件傳送一條訊息。這時,動態繫結的特性就來了。OC可以先跳過編譯,到執行的時候才動態地新增函式呼叫,在執行時才決定要呼叫什麼方法,需要傳什麼引數進去。這就是動態繫結,要實現他就必須用SEL變數繫結一個方法。最終形成的這個SEL變數就代表一個方法的引用。這裡要注意一點:SEL並不是C裡面的函式指標,雖然很像,但真心不是函式指標。SEL變數只是一個整數,他是該方法的ID。以前的函式呼叫,是根據函式名,也就是字串去查詢函式體。但現在,我們是根據一個ID整數來查詢方法,整數的查詢字自然要比字串的查詢快得多!所以,動態繫結的特定不僅方便,而且效率更高。

(3)動態載入

動態載入就是根據需求動態地載入資源。我對動態載入比較陌生,所以就沒什麼可總結的啦。等以後慢慢完善。

相關推薦

iOS執行方法呼叫原理

導語 訊息轉發 OC的動態語言特性 1動態型別 2動態繫結 3動態載入 導語 一個成熟的計算機語言必然有豐富的體系,複雜的容錯機制,處理邏輯以及判斷邏輯。但這些複雜的邏輯都是圍繞一個主線豐富和展開的,所以在學習計算機語言的時候,先掌握核心

iOS學習筆記56(Runtime)-Objective-C Runtime 執行三:方法與訊息

前面我們討論了Runtime中對類和物件的處理,及對成員變數與屬性的處理。這一章,我們就要開始討論Runtime中最有意思的一部分:訊息處理機制。我們將詳細討論訊息的傳送及訊息的轉發。不過在討論訊息之前,我們先來了解一下與方法相關的一些內容。 基礎資料型別 SEL

Objective-C Runtime 執行三:方法與訊息

前面我們討論了Runtime中對類和物件的處理,及對成員變數與屬性的處理。這一章,我們就要開始討論Runtime中最有意思的一部分:訊息處理機制。我們將詳細討論訊息的傳送及訊息的轉發。不過在討論訊息之前,我們先來了解一下與方法相關的一些內容。 基礎資料型別 SEL

[ObjectC]Runtime執行三:方法與訊息

這一章,我們就要開始討論Runtime中最有意思的一部分:訊息處理機制。我們將詳細討論訊息的傳送及訊息的轉發。 基礎資料型別 SEL SEL又叫選擇器,是表示一個方法的selector的指標,其定義如下:typedef struct objc_selector *SEL;o

iOS Runtime 執行三:訊息處理機制

前面我們討論了Runtime中對類和物件的處理,及對成員變數與屬性的處理。這一章,我們就要開始討論Runtime中最有意思的一部分:訊息處理機制。我們將詳細討論訊息的傳送及訊息的轉發。不過在討論訊息之前,我們先來了解一下與方法相關的一些內容。 基礎資料型別

反射獲取類執行物件方法

// 獲取類執行時物件方法一 Class<Student> student = Student.class; // 方法二 //注意此處吃的名字其實是一個字串,但這個字串必須是目標類的全路

iOS執行的用途一 -- 交換方法

前言 執行時的的交換方法也叫黑魔法,在許多的第三方框架都使用了,例如AFN等 步驟 一、獲得兩個需要交換的方法 Method aMethod = class_getClassMeth

Swift 執行一些方法

一、 class_getProperty 方法 class_getProperty(cls: AnyClass?, name: UnsafePointer<Int8>) 這個方法只能獲取到 用 property修飾過的屬性。如: @pro

Objective-C Runtime 執行五:協議與分類

Objective-C中的分類允許我們通過給一個類新增方法來擴充它(但是通過category不能新增新的例項變數),並且我們不需要訪問類中的程式碼就可以做到。 Objective-C中的協議是普遍存在的介面定義方式,即在一個類中通過@protocol定義介面,在另外

Objective-C Runtime 執行六:拾遺

前面幾篇基本介紹了runtime中的大部分功能,包括對類與物件、成員變數與屬性、方法與訊息、分類與協議的處理。runtime大部分的功能都是圍繞這幾點來實現的。 本章的內容並不算重點,主要針對前文中對Objective-C Runtime Reference內容遺漏

Objective-C Runtime 執行二:成員變數與屬性

在前面一篇文章中,我們介紹了Runtime中與類和物件相關的內容,從這章開始,我們將討論類實現細節相關的內容,主要包括類中成員變數,屬性,方法,協議與分類的實現。 本章的主要內容將聚集在Runtime對成員變數與屬性的處理。在討論之前,我們先介紹一個重要的概念:型別

Objective-C Runtime 執行四:Method Swizzling

理解Method Swizzling是學習runtime機制的一個很好的機會。在此不多做整理,僅翻譯由Mattt Thompson發表於nshipster的Method Swizzling一文。 Method Swizzling是改變一個selector的實際實現的

Java應用程式執行監控方法(一)——JVMTI的應用

The JVM Tool Interface (JVMTI) 是一個由JVM提供的用於開發針對Java程式開發與監控工具的程式設計介面,通過JVMTI介面(Native API)可以建立代理程式(Agent)以監視和控制 Java 應用程式,包括剖析、除錯、監控

執行鎖的機制

Java中鎖的機制 synchronized–Java語言的關鍵字,當它用來修飾一個方法或者一個程式碼塊的時候,能夠保證在同一時刻最多隻有一個執行緒執行該段程式碼。 當兩個併發執行緒訪問同一個物件Object中的這個synchronized同步程式碼塊時,

Flink執行基於Netty的網路通訊中

PartitionRequestClient 分割槽請求客戶端(PartitionRequestClient)用於發起遠端PartitionRequest請求,它也是RemoteChannel跟Netty通訊層之間進行銜接的物件。 對單一的TaskMan

Flink執行統一的資料交換物件

統一的資料交換物件 在Flink的執行引擎中,流動的元素主要有兩種:緩衝(Buffer)和事件(Event)。Buffer主要針對使用者資料交換,而Event則用於一些特殊的控制標識。但在實現時,為了在通訊層統一資料交換,Flink提供了資料交換物件——Buf

Activity在切換螢幕方法呼叫【轉】

曾經遇到過一個面試題,讓你寫出橫屏切換豎屏Activity的生命週期。現在給大家分析一下他切換時具體的生命週期是怎麼樣的: 1、新建一個Activity,並把各個生命週期打印出來 2、執行Activity,得到如下資訊 onCreate--> onSta

關於執行方法使用:objc_setAssociatedObject

- (void)viewDidLoad { [super viewDidLoad]; NSString * overview = @"overview"; self.model= [[DataModel alloc] init]; [self

IOS執行傳遞物件或者新增屬性

之前做一個專案時,碰到一個問題,為一個UITableViewCell中的不同按鈕繫結不同的物件,點選按鈕後獲取繫結的相應的物件。目前我所知道的有兩種方式可以實現該需求。1.使用IOS提供的執行時  2.之定義UIButton,為其設定屬性。下面介紹第一種方式: 一、使用執行

java 方法呼叫 方法傳參 值傳遞還是引用傳遞位元組碼

/*Java中的引數傳遞方式到底是引用傳遞還是值傳遞?java核心技術卷I裡有一個結 論我覺得挺有意思的:java中沒有引用傳遞,只有值傳遞 首先看定義: 值傳遞,是指方法接收的是呼叫者提供的值 引用傳遞,是指方法接收的是呼叫者提供的變數地址 事實上,Java中方法引