SEL、IMP和Class

轉(zhuǎn)載自:Objective-C中的SEL、IMP和Class類型

1、SEL類型

例子:

SEL say;

SEL skin;

Objective-C 在編譯的時候, 會根據(jù)方法的名字(包括參數(shù)序列),生成一個用 來區(qū)分這個方法的唯一的一個 ID,這個 ID 就是 SEL 類型的。我們需要注意的是,只要方法的名字(包括參數(shù)序列)相同,那么它們的 ID 都是相同的。就是 說,不管是超類還是子類,不管是有沒有超類和子類的關(guān)系,只要名字相同 那么 ID 就是一樣的。

在 程序執(zhí)行的時候,我們可以方便的通過方法的名字,獲取到方法的 ID 也就是我們所說的 SEL, 反之亦然。具體的使用方法如下:

[pre]1????SEL 變量名 = @selector(方法名字);2????SEL 變量名 = NSSelectorFromString(方法名字的字符串);3????NSString *變量名 = NSStringFromSelector(SEL 參數(shù));[/pre]

其中第 1 行是直接在程序里面寫上方法的名字,第 2 行是寫上方法名字的字符串,第 3 行是通 過 SEL 變量獲得方法的名字。我們得到了 SEL 變量之后,可以通過下面的調(diào)用來給一個對象發(fā) 送消息:

[對象 performSelector:SEL 變量 withObject:參數(shù) 1 withObject:參數(shù) 2];

這樣的機(jī)制大大的增加了我們的程序的靈活性,我們可以通過給一個方法傳遞 SEL 參數(shù),讓這 個方法動態(tài)的執(zhí)行某一個方法;我們也可以通過配置文件指定需要執(zhí)行的方法,程序讀取配置文 件之后把方法的字符串翻譯成為 SEL 變量然后給相應(yīng)的對象發(fā)送這個消息。

從效率的角度上來說,執(zhí)行的時候不是通過方法名字而是方法 ID 也就是一個整數(shù)來查找方法, 由于整數(shù)的查找和匹配比字符串要快得多,所以這樣可以在某種程度上提高執(zhí)行的效率。

2、IMP類型

[pre]??例子:void(*setSkinColor_Func) (id, SEL, NSString*);//定義一個函數(shù)指針(傳統(tǒng)C語言的處理方式)??????IMP say_Func;//定義一個IMP方式的函數(shù)指針(obj-C中推薦的方式)?????? 說白了IMP就是實(shí)現(xiàn)方法,給一個方法起個名字,實(shí)現(xiàn)動態(tài)調(diào)用,不用再使用[對象 message]的方式。??IMP 的定義如下:??typedef id????(*IMP)(id, SEL, ... );[/pre]

這個格式正好和我們在第一行代碼里面的函數(shù)指針的定義是一樣的。

我們?nèi)〉昧撕瘮?shù)指針之后,也就意味著我們?nèi)〉昧藞?zhí)行的時候的這段方法的代碼的入口,這樣我 們就可以像普通的 C 語言函數(shù)調(diào)用一樣使用這個函數(shù)指針。當(dāng)然我們可以把函數(shù)指針作為參數(shù) 傳遞到其他的方法,或者實(shí)例變量里面,從而獲得極大的動態(tài)性。我們獲得了動態(tài)性,但是付出 的代價就是編譯器不知道我們要執(zhí)行哪一個方法所以在編譯的時候不會替我們找出錯誤,我們只 有執(zhí)行的時候才知道,我們寫的函數(shù)指針是否是正確的。所以,在使用函數(shù)指針的時候要非常準(zhǔn) 確地把握能夠出現(xiàn)的所有可能,并且做出預(yù)防。尤其是當(dāng)你在寫一個供他人調(diào)用的接口 API 的 時候,這一點(diǎn)非常重要。

3、Class

類在 Objective-C 也為我們準(zhǔn)備了類似的機(jī)制, Class 類型。當(dāng)一個類被正確的編譯過后,在這個編譯成功的類里面,存在一個變量用于保存這 個類的信息。我們可以通過一個普通的字符串取得 這個 Class,也可以通過我們生成的對象取 得這個 Class。Class 被成功取得之后,我們可以把這個 Class 當(dāng)作一個已經(jīng)定義好的類來使用 它。這樣的機(jī)制允許我們在程序執(zhí)行的過程當(dāng)中,可以 Class 來得到對象的類,也可以在程序 執(zhí)行的階段動態(tài)的生成一個在編譯階段無法確定的一個對象。

因?yàn)?Class 里面保存了一個類的所有信息,當(dāng)然,我們也可以取得一個類的超類。關(guān)于 Class 類型,具體的使用格式如下:

[pre]1????Class 變量名 = [類或者對象 class];2????Class 變量名 = [類或者對象 superclass];3????Class 變量名 = NSClassFromString(方法名字的字符串);4????NSString *變量名 = NSStringFromClass(Class 參數(shù));[/pre]

第一行代碼,是通過向一個類或者對象發(fā)送 class 消息來獲得這個類或者對象的 Class 變量。 第二行代碼,是通過向一個類或者對象發(fā)送 superclass 消息來獲得這個類或者對象的超類的

Class 變量。

第三行代碼,是通過調(diào)用 NSClassFromString 函數(shù),并且把一個字符串作為參數(shù)來取得 Class 變量。這個在我們使用配置文件決定執(zhí)行的時候的類的時候,NSClassFromString 給我們帶來 了極大的方便。

第四行代碼,是 NSClassFromString 的反向函數(shù) NSStringFromClass,通過一個 Class 類 型作為變量取得一個類的名字。

當(dāng)我們在程序里面通過使用上面的第一,二或者第三行代碼成功的取得一個 Class 類型的變量, 比如說我們把這個變量名字命名為 myClass,那么我們在以后的代碼種可以把 myClass 當(dāng)作 一個我們已經(jīng)定義好的類來使用,當(dāng)然我們可以把這個變量作為參數(shù)傳遞到其他的方法當(dāng)中讓其 他的方法動態(tài)的生成我們需要的對象。

-----------------------------------------------------------------------

轉(zhuǎn)載自:關(guān)于Objective-C方法的IMP

一.什么是IMP

IMP是”implementation”的縮寫,它是objetive-C 方法(method)實(shí)現(xiàn)代碼塊的地址,可像C函數(shù)一樣直接調(diào)用。通常情況下我們是通過[object method:parameter]或objc_msgSend()的方式向?qū)ο蟀l(fā)送消息,然后Objective-C運(yùn)行時(Objective-C runtime)尋找匹配此消息的IMP,然后調(diào)用它;但有些時候我們希望獲取到IMP進(jìn)行直接調(diào)用。

二.Objetive-C中的Method結(jié)構(gòu)

在Objecitve-C中,在類中對每一個方法有一個在運(yùn)行時構(gòu)建的數(shù)據(jù)結(jié)構(gòu),在Objective-C 2.0中,此結(jié)構(gòu)對用戶不可見,但仍在內(nèi)部存在。

[pre]struct objc_method{??SEL method_name;??char * method_types;??IMP method_imp;};typedef objc_method Method;[/pre]每個方法有3個屬性

方法名:方法名為此方法的簽名,有著相同函數(shù)名和參數(shù)名的方法有著相同的方法名。方法類型:方法類型描述了參數(shù)的類型。IMP: IMP即函數(shù)指針,為方法具體實(shí)現(xiàn)代碼塊的地址,可像普通C函數(shù)調(diào)用一樣使用IMP。

由于Method的內(nèi)部結(jié)構(gòu)不可見,所以不能通過method->method_name的方式訪問其內(nèi)部屬性,只能Objective-C運(yùn)行時提供的函數(shù)獲取。

[pre]SEL method_getName(Method method);IMP method_getImplementation(Method method);const char * ivar_getTypeEncoding(Ivar ivar);[/pre]還有一些其他函數(shù)來獲取方法的各種屬性,具體可見Objective-C Runtime Reference

四.獲取當(dāng)前方法的默認(rèn)IMP(default IMP)

NSObject對象提供了兩個方法來獲取的IMP

[pre]- (IMP)methodForSelector:(SEL)aSelector;+ (IMP)instanceMethodForSelector:(SEL)aSelector;[/pre]使用methodForSelector方法時,若向類(class)發(fā)送消息,則aSelector應(yīng)該是類方法(class method);若向?qū)嵗龑ο?instance)發(fā)送消息,則aSelector應(yīng)該為實(shí)例對象方法(instance method)。使用instanceMethodForSelector可向類請求實(shí)例方法的IMP。

那么獲取當(dāng)前方法的IMP,可使用self對象和隱含的_cmd參數(shù)。

[pre]IMP current = [self class] instanceMethodForSelector:_cmd];[/pre]methodForSelector返回的IMP是default IMP,即發(fā)送消息時會調(diào)用的IMP。但可能有其他情況不是這樣的,但到底什么情況下呢?不懂,如下:

methodForSelector: only returns the default IMP that will be invoked by a send message but you could have arrived at the current method through a super invocation, or a direct invocation of the IMP itself. The approach shown in this post gets the correct IMP no matter how it was invoked.

四. 另一種hack的方式獲取當(dāng)前方法IMP

GCC有個內(nèi)建(build-in)函數(shù),可獲取當(dāng)前棧幀的返回地址(參數(shù)0表示當(dāng)前棧幀)。

[pre]__builtin_return_address(0)[/pre]這里有兩個假設(shè)

方法的實(shí)現(xiàn)是連續(xù)的。在方法中調(diào)用子函數(shù),在子函數(shù)中調(diào)用__builtin_return_address(0)得到的返回地址在當(dāng)前方法實(shí)現(xiàn)的內(nèi)部。

那么可以得出結(jié)論,當(dāng)前方法實(shí)現(xiàn)的起始地址(即IMP)肯定在的子函數(shù)返回地址的前面,而且會比任何其他方法離的更近。則可通過如下方法尋找IMP。

當(dāng)當(dāng)前方法中,調(diào)用一個尋找IMP的子函數(shù),將當(dāng)前類和_cmd參數(shù)傳遞給子函數(shù);在子函數(shù)中使用__builtin_return_address(0)獲得返回地址,遍歷當(dāng)前類和當(dāng)前父類的所有方法,尋找method_name與_cmd相等,而method_imp在__builtin_return_address(0)之前,且離其最近,則此IMP即為當(dāng)前的的IMP。

[pre]#import IMP impOfCallingMethod(id lookupObject, SEL selector){????NSUInteger returnAddress = (NSUInteger)__builtin_return_address(0);????NSUInteger closest = 0;????// Iterate over the class and all superclasses????Class currentClass = object_getClass(lookupObject);????while (currentClass)????{????????// Iterate over all instance methods for this class????????unsigned int methodCount;????????Method *methodList = class_copyMethodList(currentClass, &methodCount);????????unsigned int i;????????for (i = 0; i < methodCount; i++)????????{????????????// Ignore methods with different selectors????????????if (method_getName(methodList) != selector)????????????{????????????????continue;????????????}????????????// If this address is closer, use it instead????????????NSUInteger address = (NSUInteger)method_getImplementation(methodList);????????????if (address < returnAddress && address > closest)????????????{????????????????closest = address;????????????}????????}????????free(methodList);????????currentClass = class_getSuperclass(currentClass);????}????return (IMP)closest;}[/pre]其使用方法為:

[pre]- (void)myMethodWithParam1:(int)someParameter andParam2:(int)otherParameter{?? IMP myImplementation = impOfCallingInstanceMethod([self class], _cmd);?? ...}[/pre]另外還有更快速的方法:獲取當(dāng)前方法的返回地址,然后通過此地址回溯到父函數(shù)的IMP內(nèi)部,在父函數(shù)IMP中尋找調(diào)用當(dāng)前函數(shù)的位置,即可知道當(dāng)前函數(shù)的IMP,不過此方法是平臺相關(guān)的。

注:此方法假設(shè)IMP地址與子函數(shù)返回地址之間是連續(xù)的,中間不會有其他方法IMP。如果將impOfCallingMethod放到block代碼中,則此條件不再滿足,不再適用。(待驗(yàn)證)

參考:

IMP of the current method

Objective-C Runtime Reference

NSObject Class Reference

--------------------------------------------------------------------------------------------------

轉(zhuǎn)載自:Objective-C 2.0 with Cocoa Foundation--- 5,Class類型,選擇器Selector以及函數(shù)指針

這篇文章非常好,直接移步鏈接吧~~~

轉(zhuǎn)載自:IOS SEL (@selector) 原理及使用總結(jié)(一)

IOS SEL (@selector) 原理及使用總結(jié)(二)

轉(zhuǎn)載自:Objective-C的消息傳遞機(jī)制

這篇文章也是超贊,講到多參數(shù)時的調(diào)用方法。

轉(zhuǎn)載自:深入淺出 Cocoa 之消息

------------------------------------

更多文章可尋 runtime機(jī)制。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 778評論 0 1
  • 繼上Runtime梳理(四) 通過前面的學(xué)習(xí),我們了解到Objective-C的動態(tài)特性:Objective-C不...
    小名一峰閱讀 770評論 0 3
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,231評論 0 7
  • 本文轉(zhuǎn)載自:http://southpeak.github.io/2014/10/25/objective-c-r...
    idiot_lin閱讀 949評論 0 4
  • 文中的實(shí)驗(yàn)代碼我放在了這個項(xiàng)目中。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 944評論 0 6