Objective-C Runtime 的一些理解

來源:互聯網
上載者:User

標籤:

  • 1. [ ] 表示發送訊息

[receiver message] 會被編譯器轉化為:
objc_msgSend(receiver, selector)

如果含有參數
objc_msgSend(receiver, selector, arg1, arg2, ...)

 

  • 2. Runtime 術語

 

 

執行個體對象有個isa的屬性,指向Class, 而Class裡也有個isa的屬性, 指向meteClass. 這裡就有個點, 在Objective-C中任何的類定義都是對象,他們的關係如:

 

  • 擷取屬性列表:(指標的指標)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) // 類中objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount) // 協議中

 

  • 擷取屬性名稱:
const char *property_getName(objc_property_t property)

 

  • 擷取屬性:
const char *property_getAttributes(objc_property_t property)

例如:

/*----------------------------------------------------------------------------------------------*/@interface Leader : NSObject {float alone;}@property float alone;@end// 擷取類中的屬性列- (void)getPropertys() {id LenderClass = objc_getClass("Leader");unsigned int outCount;objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);for(int i=0; i<outCount; i++) {objc_property_t property = properties[i];fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));}}}
  • objc_msgSend 函數

發送訊息時,編譯器會根據情況在下面4個函數中選一個來調用。
objc_msgSend: 訊息傳回值為簡單值
objc_msgSend_stret: 訊息傳回值為 struct

objc_msgSendSuper: 訊息發送給父類
objc_msgSendSuper_stret

 

  • 訊息發送步驟 

1. 檢測這個 selector 是不是要忽略的。比如有了記憶體回收就不理會 retain, release 這些函數了。
2. 檢測這個 target 是不是 nil 對象。ObjC 的特性是允許對一個 nil 對象執行任何一個方法不會 Crash,因為會被忽略掉。
3. 如果上面兩個都過了,那就開始尋找這個類的 IMP,先從 cache 裡面找,完了找得到就跳到對應的函數去執行。
4. 如果 cache 找不到就找一下方法分發表。
5. 如果分發表找不到就到超類的分發表去找,一直找,直到找到NSObject類為止。
6. 如果還找不到就要開始進入動態方法解析:
6.1 是否實現了 resolveInstanceMethod,是則執行這裡定位的方法
6.2 是否實現了 forwardingTargetForSelector,是則執行這裡定位的方法
6.3 是否實現了 forwardInvocation,是則執行這裡定位的方法
6.4 都沒有實現,返回 message not handle 訊息。

 動態方法解析函數執行流程如下:

  • self 如何取到調用方法的對象 

self 為方法中的隱含參數,self 是在方法運行時被偷偷的動態傳入的。

當objc_msgSend找到方法對應的實現時,它將直接調用該方法實現,
並將訊息中所有的參數都傳遞給方法實現,同時,它還將傳遞兩個隱藏的參數:
1. 接收訊息的對象:即 self 指向的內容

2. 方法選取器:即 _cmd 指向的內容

 

  • 動態方法解析

我們可以通過分別重載 resolveInstanceMethod: 和 resolveClassMethod: 方法分別添加執行個體方法實現和類方法實現。
因為當 Runtime 系統在 Cache 和方法分發表中(包括超類)找不到要執行的方法時,Runtime 會調用 resolveInstanceMethod:
或 resolveClassMethod: 來給程式員一次動態添加方法實現的機會。我們需要用 class_addMethod 函數完成向特定類添加特定
方法實現的操作:

    void dynamicMethodIMP(id self, SEL _cmd) {        // implementation ....    }    @implementation myClass     + (BOOL) resolveInstanceMethod:(SEL)aSEL {        if (aSEL == @selector(resolveThisMethodDynamically)) {            class_addMethod([self class], aSEL, (IMP)dynamicMethodIMP, "[email protected]:")        }    }    @end

 

  • 重新導向

在訊息轉寄機制執行前,Runtime 系統會再給我們一次偷梁換柱的機會,
即通過重載- (id)forwardingTargetForSelector:(SEL)aSelector方法替換訊息的接受者為其他對象

        - (id)forwardingTargetForSelector:(SEL)aSelector {        if (aSelector == @selector(myMethod)) {            return alternateObject;        }        return [super forwardingTargetForSelector: aSelector];    }

 

  • 轉寄

通過重寫 forwardInvocation: 方法實現。

1. 在 forwardInvocation: 訊息發送前,Runtime系統會向對象發送 methodSignatureForSelector: 訊息,
並取到返回的方法簽名用於產生NSInvocation對象。所以我們在重寫forwardInvocation:的同時也要重寫
methodSignatureForSelector: 方法,否則會拋異常。

2. 當一個對象由於沒有相應的方法實現而無法響應某訊息時,運行時系統將通過 forwardInvocation: 訊息通知該對象。
每個對象都從 NSObject 類中繼承了 forwardInvocation: 方法。然而,NSObject 中的方法實現只是簡單地調用了
doesNotRecognizeSelector:。通過實現我們自己的forwardInvocation:方法,我們可以在該方法實現中將訊息轉寄給其它對象。

3. forwardInvocation: 方法就像一個 不能識別的訊息 的分發中心,將這些訊息轉寄給不同接收對象。
或者它也可以象一個運輸站將所有的訊息都發送給同一個接收對象。它可以將一個訊息翻譯成另外一個訊息,
或者簡單的”吃掉“某些訊息,因此沒有響應也沒有錯誤。forwardInvocation: 方法也可以對不同的訊息提供同樣的響應,
這一切都取決於方法的具體實現。該方法所提供是將不同的對象連結到訊息鏈的能力。‘

    - (void)forwardInvocation:(NSInvocation *)anInvocation {        if ([someOtherObject respondsToSelector:[anInvocation selector]]) {            [anInvocation invokeWithTarget:someOtherObject];        } else {            [super forwardInvocation: anInvocation];        }    }

 

  • 轉寄與繼承的區別與聯絡

聯絡:
1. OC 沒有多繼承,利用訊息轉寄可以實現多繼承的功能。
例如:A 繼承了 B 和 C,(A : B , C)那麼 A 就同時擁有了 B 和 C 中的方法。
A 將訊息轉寄給 B 和 C 也能實現同樣的功能。
一個對象把訊息轉寄出去,就好似它把另一個對象中的方法借過來或是“繼承”過來一樣。

區別:
1. 像 respondsToSelector: 和 isKindOfClass: 這類方法只會考慮繼承體系,不會考慮轉寄鏈。

 

  • Objective-C Associated Objects

在 OS X 10.6 之後,Runtime系統讓Objc支援向對象動態添加變數。涉及到的函數有以下三個:

    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);    id objc_getAssociatedObject(id object, const void *key);    void objc_removeAssociatedObjects(id object);

其中關聯政策是一組枚舉常量, 這些常量對應著引用關聯值的政策,也就是 Objc 記憶體管理的引用計數機制。

    enum {       OBJC_ASSOCIATION_ASSIGN  = 0,       OBJC_ASSOCIATION_RETAIN_NONATOMIC  = 1,       OBJC_ASSOCIATION_COPY_NONATOMIC  = 3,       OBJC_ASSOCIATION_RETAIN  = 01401,       OBJC_ASSOCIATION_COPY  = 01403    };

 

參考:

http://yulingtianxia.com/blog/2014/11/05/objective-c-runtime/index.html

http://blog.csdn.net/uxyheaven/article/details/38113901

http://southpeak.github.io/blog/2014/10/25/objective-c-runtime-yun-xing-shi-zhi-lei-yu-dui-xiang/

Objective-C Runtime 的一些理解

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.