SEL、IMP、Method

先说一个大家都熟悉的 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,就会走消息转发流程

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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 4,162评论 0 1
  • 我们知道iOS程序的入口函数在main.其实mian只是苹果给我们的"直观能够感受"的入口,在执行main之前,编...
    偶尔登南山阅读 4,933评论 0 2
  • 文中的实验代码我放在了这个项目中。 以下内容是我通过整理[这篇博客] (http://yulingtianxia....
    茗涙阅读 4,451评论 0 6
  • 继上Runtime梳理(四) 通过前面的学习,我们了解到Objective-C的动态特性:Objective-C不...
    小名一峰阅读 4,114评论 0 3
  • Runtime是一套比较底层的纯C语言API,包含了很多底层的C语言API。在我们平时编写的OC代码中,程序运行时...
    这个年纪的情愫丶阅读 3,788评论 5 3