一、概述
- 對于實例方法变泄,每個實例的 isa 指針指向著對應(yīng)類對象令哟,而每一個類對象中都一個對象方法列表。
- 對于類方法杖刷,每個類對象的 isa 指針都指向著對應(yīng)的元對象励饵,而每一個元對象中都有一個類方法列表。
- 方法列表中記錄著方法的名稱滑燃,方法實現(xiàn)役听,以及參數(shù)類型,其實 selector 本質(zhì)就是方法名稱表窘,通過這個方法名稱就可以在方法列表中找到對應(yīng)的方法實現(xiàn)典予。
- 當(dāng)我們發(fā)送一個消息給一個NSObject對象時,這條消息會在對象的類對象方法列表里查找
- 當(dāng)我們發(fā)送一個消息給一個類時乐严,這條消息會在類的Meta Class對象的方法列表里查找
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class
const char *name
long version
long info
long instance_size
struct objc_ivar_list *ivars
struct objc_method_list **methodLists
struct objc_cache *cache
struct objc_protocol_list *protocols
#endif
} OBJC2_UNAVAILABLE;
#這里聲明了一個指向struct objc_method_list指針的指針瘤袖,可以包含類方法列表和實例方法列表
二、具體實現(xiàn)
在尋找IMP的地址時昂验,runtime提供了兩種方法:
IMP class_getMethodImplementation(Class cls, SEL name);
IMP method_getImplementation(Method m);
根據(jù)官方描述捂敌,第一種方法可能會更快一些
@note \c class_getMethodImplementation may be faster than \c method_getImplementation(class_getInstanceMethod(cls, name)).
-
2.1、 IMP class_getMethodImplementation(Class cls, SEL name)
對于第一種方法而言既琴,類方法和實例方法實際上都是通過調(diào)用class_getMethodImplementation()
來尋找IMP地址的占婉,不同之處在于傳入的第一個參數(shù)不同(假設(shè)有一個類A)
`類方法`
class_getMethodImplementation(objc_getMetaClass("A"),@selector(methodName));
`實例方法`
class_getMethodImplementation([A class],@selector(methodName));
通過該傳入的參數(shù)不同,找到不同的方法列表甫恩,方法列表中保存著下面方法的結(jié)構(gòu)體逆济,結(jié)構(gòu)體中包含這方法的實現(xiàn),selector本質(zhì)就是方法的名稱,通過該方法名稱奖慌,即可在結(jié)構(gòu)體中找到相應(yīng)的實現(xiàn)抛虫。
Selector、Method 和 IMP 的關(guān)系可以這樣描述:
在運(yùn)行期分發(fā)消息简僧,方法列表中的每一個實體都是一個方法(Method)建椰,它的名字叫做選擇器(SEL),對應(yīng)著一種方法實現(xiàn)(IMP)涎劈。
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;
struct objc_method {
SEL method_name; // 方法選擇器广凸。
char *method_types; // 存儲著方法的參數(shù)類型和返回值類型阅茶。
IMP method_imp; // 函數(shù)指針蛛枚。
}
-
2.2、 IMP method_getImplementation(Method m);
而對于第二種方法而言脸哀,傳入的參數(shù)只有method蹦浦,區(qū)分類方法和實例方法在于封裝method的函數(shù)
`類方法`
Method class_getClassMethod(Class cls, SEL name)
`實例方法`
Method class_getInstanceMethod(Class cls, SEL name)
`最后調(diào)用`
IMP method_getImplementation(Method m)
`獲取IMP地址`
-
2.3 兩種方法代碼示例
這里有一個叫Test的類,在初始化方法里撞蜂,調(diào)用了三次getIMPFromSelector:自定義方法盲镶,第一個aaa方法是不存在的,test1和test2分別為實例方法和類方法
#import "Test.h"
#import <objc/runtime.h>
@implementation Test
- (instancetype)init {
self = [super init];
if (self) {
[self getIMPFromSelector:@selector(aaa)];
[self getIMPFromSelector:@selector(test1)];
[self getIMPFromSelector:@selector(test2)];
}
return self;
}
- (void)test1 {
NSLog(@"test1");
}
+ (void)test2 {
NSLog(@"test2");
}
- (void)getIMPFromSelector:(SEL)aSelector {
NSLog(@"第一種 IMP class_getMethodImplementation(Class cls, SEL name)")
NSLog(@"實例方法")
IMP insttanceIMP1 = class_getMethodImplementation(objc_getClass("Test"), aSelector);
NSLog(@"類方法")
IMP classIMP1 = class_getMethodImplementation(objc_getMetaClass("Test"), aSelector);
NSLog(@"第二種 IMP method_getImplementation(Method m)")
NSLog(@"實例方法")
Method insttanceMethod = class_getInstanceMethod(objc_getClass("Test"), aSelector);
IMP insttanceIMP2 = method_getImplementation(insttanceMethod);
NSLog(@"類方法")
Method classMethod1 = class_getClassMethod(objc_getClass("Test"), aSelector);
IMP classIMP2 = method_getImplementation(classMethod1);
NSLog(@"類方法")
Method classMethod2 = class_getClassMethod(objc_getMetaClass("Test"), aSelector);
IMP classIMP3 = method_getImplementation(classMethod2);
NSLog(@"insttanceIMP1:%p insttanceIMP2:%p classIMP1:%p classIMP2:%p classIMP3:%p", insttanceIMP1, insttanceIMP2, classIMP1, classIMP2, classIMP3);
}
@end
然后我實例化了Test的對象蝌诡,打印信息如下
Test *objc = [[Test alloc] init];
insttanceIMP1:0x7fff50ba5080 insttanceIMP2:0x0 classIMP1:0x7fff50ba5080 classIMP2:0x0 classIMP3:0x0
insttanceIMP1:0x10ebbac90 insttanceIMP2:0x10ebbac90 classIMP1:0x7fff50ba5080 classIMP2:0x0 classIMP3:0x0
insttanceIMP1:0x7fff50ba5080 insttanceIMP2:0x0 classIMP1:0x10ebbacc0 classIMP2:0x10ebbacc0 classIMP3:0x10ebbacc0
- 在調(diào)用第一種方法
IMP class_getMethodImplementation(Class cls, SEL name)
時溉贿,無法找到對應(yīng)實現(xiàn)時返回的相同的一個地址0x7fff50ba5080,無論該方法是在實例方法或類方法浦旱,返回的地址都是相同的(但是每次運(yùn)行該程序時返回的地址并不相同)宇色; - 在調(diào)用第二種方法
IMP class_getMethodImplementation(Class cls, SEL name)
時,如果找不到對應(yīng)的實現(xiàn)颁湖,則返回0x0宣蠕。
還有一點(diǎn)有趣的是:
- 調(diào)用
class_getClassMethod()
的第一個參數(shù)無論傳入objc_getClass()
還是objc_getMetaClass()
,最終調(diào)用method_getImplementation()
都可以成功的找到類方法
的實現(xiàn)甥捺。 - 調(diào)用
class_getInstanceMethod()
的第一個參數(shù)如果傳入objc_getMetaClass()
抢蚀,再調(diào)用method_getImplementation()
時無法找到實例方法
的實現(xiàn),可以找到類方法
的實現(xiàn)镰禾。