先说一个大家都熟悉的 SEL
SEL
:表示一个selector
的指针。系统在编译过程中,会根据方法的名字以及参数序列生成一个用来区分这个方法的唯一ID编号,这个 ID 就是SEL
类型的。我们需要注意的是,只要方法的名字和参数序列完全相同,那么它们的 ID编号就是相同的。(这里我先提出个问题,既然SEL
是方法的唯一标识,那不同的类调用名字相同的方法怎么办呢?稍后解答。。。)
常见的几种方法来获取/创建选择器:
SEL aSel = @selector(didReceiveMemoryWarning);
SEL a_sel = NSSelectorFromString(@"didReceiveMemoryWarning");
SEL a_Sel = sel_registerName("didReceiveMemoryWarning");
打印结果:
[5192:325263] 0x1214054bc___0x1214054bc___0x1214054bc
SEL的操作函数:
// 比较两个选择器
BOOL sel_isEqual ( SEL lhs, SEL rhs );
//判断方法名是否映射到某个函数实现上
BOOL sel_isMapped(SEL sel);
IMP:
IMP
即Implementation
,为指向函数实现的指针,如果我们能够获取到这个指针,则可以直接调用该方法,充分证实了它就是一个函数的指针。
代码定义如下:
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif
其参数包含id,SEL,后面试实际的参数列表。
那么,XX调用了XXX方法,其参数为XX都确定下来了。
还记得我们上面提出的问题吗?既然SEL
是方法的唯一标识,那不同的类调用名字相同的方法怎么办呢?通过IMP
就可以解决我们的疑问了。SEL
就是为了查找方法的最终实现IMP
获取IMP
的方法:
//通过Method获取IMP
IMP method_getImplementation(Method m);
// 返回方法的具体实现
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
获取到IMP
之后可直接调用方法:
SEL aSel = @selector(didReceiveMemoryWarning);
Method method = class_getInstanceMethod([self class], aSel);
IMP imp = method_getImplementation(method);
((void (*) (id, SEL)) (void *)imp)(self, aSel);
Method:
通过名字就能知道这是方法的意思,用于表示类定义中的方法,它的结构体中包含一个SEL
和IMP
,相当于在SEL
和IMP
之间作了一个映射。Method
其实就是objc_method
的结构体指针:
typedef struct objc_method *Method;
//结构体定义如下:
struct objc_method {
SEL _Nonnull method_name OBJC2_UNAVAILABLE;//方法名
char * _Nullable method_types OBJC2_UNAVAILABLE;//参数类型以及返回值类型编码
IMP _Nonnull method_imp OBJC2_UNAVAILABLE;//方法实现指针
} OBJC2_UNAVAILABLE;
获取Method
的方法:
// 获取实例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 获取类方法
Method class_getClassMethod ( Class cls, SEL name );
// 获取所有方法的数组
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
总结:
消息机制:任何方法的调用本质就是发送一个消息。编译器会将消息表达式
[receiver message]
转化为一个消息函数objc_msgSend(receiver, selector)
。
objc_msgSend
做了如下事情:通过对象的
isa
指针获取类的结构体。
在结构体的方法表里查找方法的selector
。
如果没有找到selector
,则通过objc_msgSend
结构体中指向父类的指针找到父类,并在父类的方法表里查找方法的selector
。
依次会一直找到NSObject
。
一旦找到selector
,就会获取到方法实现IMP
。
传入相应的参数来执行方法的具体实现。
如果最终没有定位到selector,就会走消息转发流程
下一篇和大家聊一聊消息转发流程!