OC基础

SEL、IMP、Method

2019-07-19  本文已影响0人  Su_per_man

先说一个大家都熟悉的 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:

IMPImplementation,为指向函数实现的指针,如果我们能够获取到这个指针,则可以直接调用该方法,充分证实了它就是一个函数的指针。

代码定义如下:
#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:

通过名字就能知道这是方法的意思,用于表示类定义中的方法,它的结构体中包含一个SELIMP,相当于在SELIMP之间作了一个映射。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,就会走消息转发流程

下一篇和大家聊一聊消息转发流程!

上一篇 下一篇

猜你喜欢

热点阅读