SEL
首先,SEL是一个选择器,我们来看看SEL在runtime中的定义:
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
可见SEL是一个指向objc_selector结构体的指针。
那么selector有什么用呢,当我们执行方法调用的时候,OC会根据selector来执行对应的方法。
但是,两个类之间,不管他们有没有关系,只要他们类中的方法名一样,那么他们的SEL是同一个值。!!!
这里我用一个例子来演示一下:
首先定义一个Person类,并定义一个run方法:
@implementation Person
-(void) run{
NSLog(@"I like running");
}
@end
然后在主函数中,我通过两种不同的方法获取方法run的selector,并打印这两个selector的地址。
int main(int argc, const char * argv[]) {
@autoreleasepool {
//方法一获取run的selector
SEL selector = @selector(run);
NSLog(@"selector address : %p",selector);
//方法2获取run的selector
const char * methodName = [@"run" UTF8String];
SEL selectorFromName = sel_registerName(methodName);
NSLog(@"selectorFromName address : %p",selectorFromName);
//执行后,可以看到,通过两种不同方法获得的selector,他们的地址是一样的
}
return 0;
}
执行结果:
2021-03-18 14:27:10.369837+0800 Project06[83997:4736657] selector address : 0x7fff7c300f34
2021-03-18 14:27:10.370205+0800 Project06[83997:4736657] selectorFromName address : 0x7fff7c300f34
Program ended with exit code: 0
可以看到,两个selector的地址是一模一样的,所以他们具有一样的selector!!
因此,在OC中,同一个中不允许出现同名的方法,即使他们的方法参数不一样也不行,因为这会导致他们的SEL相同。(即OC中不存在函数的重载!)
那么问题来了,两个方法的selector的一样,那么我们在执行类的对应方法的时候,是如何定位到指定类的指定方法的呢?
这时候,IMP的作用就体现了。
IMP
IMP:implementation (实现体)
其在runtime源码中的定义是:
typedef void (*IMP)(void /* id, SEL, ... */ );
可见IMP是一个函数指针。
如图,当我们在两个类都调用run方法
而每一个方法在runtime都被定义为一个结构体:
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;
char * _Nullable method_types OBJC2_UNAVAILABLE;
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
}
,结构体有三个元素,虽然他们的SEL是一样的,但是根据IMP不一样,我们就可以定位到指定类的指定方法了。而不会出现不同类中的同名方法定位模糊的问题。