Objective-c方法調用流程

來源:互聯網
上載者:User

Objective-c方法調用流程

  Objective-c是一門動態語言,動態兩個字主要就體現在我們調用方法的時候,運行時回動態尋找方法,然後調用相應的函數地址。運行時是整個Objective-c程式的基石,有了它我們的程式才能正常運行起來。

  NSObject是Cocoa中絕大部分類的基類,它主要是提供了序列話,拷貝對象,以及支援運行時動態識別的架構。

  在Objective-c中每一個類對象最開始的位置都會有一個isa指標,該指標指向一塊記憶體地區,該部分主要包含兩部分資訊:

  1、指向父類的指標。

  2、自身的方法分發表。

  有了這兩部分,Objective-c的方法的調用流程就可以跑起來了。當我們調用一個對象的某一個方法的時候,首先會在當前類的分發表中尋找該方法,如果找不到對應的方法,然後再去其父類中尋找該方法,依次類推直到找到對應的方法為止,流程圖如下:

  你可能會想到,如果一個類有很深的繼承層次,每次去調用根類的某個函數,豈不是都要做很多次尋找。理論上是這個樣子的,不過runtime也並非那麼傻,它會為每一個類(不是對象)維護一個經常調用的方法的列表,只要調用過就會緩衝起來(官方沒有明確說明緩衝機制),這樣當程式運行穩定以後整個方法調用的過程就會更加高效。

  通過學習官方文檔Objective-C Runtime Programming Guide,可以發現其實所有的selector調用最後都會轉化為C類型的函數調用。舉個例子我們建立了一個A類型的對象aSample,然後調用其test方法([aSample test]),編譯的時候,編譯器就會將該調用轉化為objc_send(aSample, selector)的形式,runtime會調用test方法實現所對應的函數地址。該函數的參數包含了兩個隱含的參數self以及_cmd,其中self指向調用該方法的對象,_cmd則代表要調用的方法。

  前面提到了NSObject提供了很多遍曆的方法可以和運行時進行互動,其中有個方法methodForSelector,通過它我們可以直接擷取到指定的方法對應的函數指標。通常我們直接使用Objective-c方式的方法調用就可以了,但有時程式中可能會頻繁的調用某一個方法,為了提高效率。我們可以直接擷取到方法對應的函數地址,然後直接調用該函數,這樣就少了動態識別的時間。

  下面舉個例子:

// 父類中定義該方法- (void)testMethod{    //NSLog(@"the implementation of BaseSample!!!");    int a = 5 / 2.0f;    a = ~a;}// 測試方法,分別使用兩種方法調用1億次- (void)test{    void (*methodAddress)(id,SEL);    methodAddress = (void(*)(id,SEL))[self methodForSelector:@selector(testMethod)];        NSLog(@"Invoke with Method Address start!!!");    for (int i = 0; i < 100000000; ++i) {        methodAddress(self, @selector(testMethod));    }    NSLog(@"Invoke with Method Address finish!!!");        NSLog(@"Invoke with direct selector start!!!");    for (int i = 0; i < 100000000; ++i) {        [self testMethod];    }    NSLog(@"Invoke with direct selector finish!!!");}

  運行結果如:

  可以看出調用時間:使用函數地址調用共花費0.151s,直接調用方法花費0.734s。時間是有一點兒差距,但是已經微乎其微了,這也從側面說明了runtime的緩衝機制還是很給力的。

  當我們調用某一個不存在的方法的時候,程式會crush,在命令列提示“unrecognized selector sent to instance 0xxxxxxx”,並拋出“NSInvalidArgumentException”的異常。當調用一個對象不能識別的方法時,runtime會一直沿著類的繼承關係往基類方向尋找,直到NSObject類,如果還是識別不了該方法的話,再拋出異常之前runtime還給我們了最後一次“補救”的機會。它會先調用forwardInvocation方法,如果我們想把這個方法異常調用捕獲並傳遞到其他地方的話,可以在類中重寫該方法。NSObject對於forwardInvocation方法的預設實現是調用doesNotRecognizeSelector方法,而doesNotRecognizeSelector則是直接拋出異常。

  當調用forwardInvocation的時候會傳入一個NSInvocation的參數,該參數標識了調用的方法的對象以及調用的方法,並對該方法的調用結果進行封裝。我們重寫forwardInvocation方法的時候,還必須同時重寫methodSignatureForSelector方法,該方法返回表示一個方法的字串,具體如何構建請看Type Encodings。

  下面舉一個簡單的重寫forwardInvocation的例子:

#pragma mark-#pragma mark 重寫 ForwardInvocation- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{    if ([self respondsToSelector:aSelector]) {        return [super methodSignatureForSelector:aSelector];    }    else {        return [NSMethodSignature signatureWithObjCTypes:"v@:"];    }}- (void)forwardInvocation:(NSInvocation *)anInvocation{    NSLog(@"Hello unreconginized selector!");}// 在init中調用一個不存在的方法hello- (id)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        // Initialization code        [self hello];    }    return self;}

  上面的例子,截獲了不能識別的方法調用,建立了一個返回void類型的方法簽名,當調用不能識別的方法的時候列印簡單的日誌。當然在程式中最好不要這麼做,特別是開發的時候,大部分時候我們更希望能夠儘早的發現這種調用錯誤。

  總結:Objective-c方法調用的流程就基本介紹完了,有什麼寫的不對的地方歡迎指正。

註:本文歡迎轉載,轉載請註明出處。同時歡迎加我qq,期待與你一起探討更多問題。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.