先说一个大家都熟悉的 SEL

ID 就是```SEL```类型的。我们需要注意的是,只要方法的名字和参数序列完全相同,那么它们的 ID编号就是相同的。(这里我先提出个问题,既然```SEL```是方法的唯一标识,那不同的类调用名字相同的方法怎么办呢?稍后解答。。。)
1
>#####常见的几种方法来获取/创建选择器:

SEL aSel = @selector(didReceiveMemoryWarning);
SEL a_sel = NSSelectorFromString(@”didReceiveMemoryWarning”);
SEL a_Sel = sel_registerName(“didReceiveMemoryWarning”);

1
打印结果:

[5192:325263] 0x1214054bc___0x1214054bc___0x1214054bc

1
SEL的操作函数:

// 比较两个选择器
BOOL sel_isEqual ( SEL lhs, SEL rhs );
//判断方法名是否映射到某个函数实现上
BOOL sel_isMapped(SEL sel);

1
2
3
4
5
6



##IMP:
```IMP```即```Implementation```,为指向函数实现的指针,如果我们能够获取到这个指针,则可以直接调用该方法,充分证实了它就是一个函数的指针。
#####代码定义如下:

#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (IMP)(void / id, SEL, … */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, …);
#endif

1
2
3
4
其参数包含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 );

1
获取到```IMP```之后可直接调用方法:

SEL aSel = @selector(didReceiveMemoryWarning);
Method method = class_getInstanceMethod([self class], aSel);
IMP imp = method_getImplementation(method);
((void (*) (id, SEL)) (void *)imp)(self, aSel);

1
2
3

##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;

1
2

获取```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,就会走消息转发流程

![image-20230109102526365](/Users/david/Library/Application Support/typora-user-images/image-20230109102526365.png)