轉(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)證)
參考:
--------------------------------------------------------------------------------------------------
轉(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ī)制。