iOS_selector、SEL、IMP、Method都是什么,以及之间的关系

本文详细介绍了Objective-C中SEL、IMP和Method的概念及其关系。SEL是表示方法选择器的不透明类型,相当于方法名;IMP是方法实现的指针,指向方法的实际代码;Method则包含了方法名、类型和实现。它们共同构成了Objective-C运行时消息传递的基础。通过SEL获取IMP可以直接调用方法,而Method结构体提供了对方法的完整描述。
摘要由CSDN通过智能技术生成


​ 在 Objective-C中使用发送消息的形式来调用方法,其中涉及到 Runtime库中定义的 SELIMPMethod,它们分别表示什么,以及它们之间的关系。

一、SEL(selector)

selector其实只是一个SEL类型的一个实例:

@property SEL selector;

SEL:一个不透明的类型,代表方法的选择器/选择子。定义如下:

typedef struct objc_selector *SEL;
// 是一个指向 objc_selector 结构体的指针
// objc_selector 未开源

​ 在源码中没有直接找到objc_selector的定义,从一些书籍上与Blog上看到可以将SEL理解为一个char*指针。

// GNU OC 中的 objc_selector
struct objc_selector {  
  void *sel_id;  
  const char *sel_types;  
};  

​ 在运行时,方法选择器用来表示方法的名字。一个方法选择器就是一个C字符串,在OC的运行时被注册。编译器生成选择器在类加载时由运行时自动映射。

​ 您可以在运行时添加新的选择器,并使用sel_registerName函数检索现有的选择器。

​ 必须从sel_registerName@selector()(OC编译器指令)返回的值,获得选择器(用NSSelectorFromString也可以)。不能简单地将C字符串转换为SEL

测试:

// 获得SEL的三种方式
SEL selA = @selector(setTitle:);
SEL selB = sel_registerName("setTitle:");
SEL selC = NSSelectorFromString(@"setTitle:");

// 输出
(lldb) po selA
"setTitle:"
(lldb) po selB
"setTitle:"
 (lldb) po selC
"setTitle:"

​ 还可以使用sel_getNameSEL转回成Char*

NSLog(@"%s", sel_getName(selB));
// setTitle:

​ 从sel_getName的源码可以看出SELconst char * 是可以相互转化的:

const char *sel_getName(SEL sel)  {
    if (!sel) return "<null selector>";
    return (const char *)(const void*)sel;
}

注意:

​ 不同类中相同名字的方法所对应的方法选择器是相同的,即使方法名字相同而变量类型不同,也会导致它们具有相同的方法选择子。比如:

- (void)caculate(NSInteger)num;
- (void)caculate(CGFloat)num;

这样定义会报错,因为这两个方法的选择器是一样的,因此OC中不允许方法重载(因为只能靠方法名来标识方法)。

触发 SEL,并消除 warning:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
	BOOL hasIvar = [self performSelector:hasIvarSel];
#pragma clang diagnostic pop

二、IMP(implementation)

​ 指向方法实现的首地址的指针。源码里实现如下:(可以看得出来是对方法类型起了一个别名。)

/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

IMP的数据类型是指针,指向方法实现开始的位置。这样的方法使用标准的C调用协议(此协议是为当前CPU体系结构实现的)。

  • 第一个参数是指向self的指针(实例的内存<实例方法> 或 元类的指针<类方法>);
  • 第二个参数是方法的选择器;
  • 接下来是方法的参数。

一些使用:

// 返回方法的具体实现
IMP class_getMethodImplementation(Class cls, SEL name);
IMP class_getMethodImplementation_stret(Class cls, SEL name);
// 类实例是否响应指定的selector
BOOL class_respondsToSelector(Class cls, SEL sel);

IMP的高级作用

​ 既然上述元素都确定下来了,那么就可以直接绕过Runtime的消息传递机制,直接执行IMP指向的函数了。省去了一些列的查找,直接向对象发送消息,效率会高一些。

// 根据代码块获取IMP, 其实就是代码块与IMP关联
IMP imp_implementationWithBlock(id block) 
// 根据Method获取IMP
IMP method_getImplementation(Method m) 
// 根据SEL获取IMP
[[objc Class] instanceMethodForSelector:SEL] 

​ 当我们获取一个方法的IMP后,可以直接调用IMP

IMP imp = method_getImplementation(Method m)// result保存方法的返回值,id表示调用这个方法的对象,SEL是Method的选择器,argument是方法的参数。
id result = imp(id, SEL, argument);

三、Method

​ 一个不透明的类型,表示类中定义的方法。定义如下:

/// An opaque type that represents a method in a class definition.
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是一个指向objc_method结构体类型的指针。objc_method结构体中定一个三个属性:

  • method_nameSEL类型(选择器),表示方法名的字符串。

  • method_typeschar*类型的,表示方法的类型;包含返回值和参数的类型。

  • method_impIMP类型,指向方法实现地址的指针。

Method操作函数如下:

方法操作主要有以下函数:
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types); // 添加方法
Method class_getInstanceMethod(Class cls, SEL name); // 获取实例方法
Method class_getClassMethod(Class cls, SEL name); // 获取类方法
Method *class_copyMethodList(Class cls, unsigned int *outCount); // 获取所有方法的数组
// 替代方法的实现
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types); 
// 交换两个方法的实现
method_exchangeImplementations(Method m1, Method m2)

总结

SEL:就是一个字符串(Char*类型),表示方法的名字

IMP:就是指向方法实现首地址的指针

Method:是一个结构体,包含一个SEL表示方法名、一个IMP指向函数的实现地址、一个Char*表示函数的类型(包括返回值和参数类型)

  • SELIMPMethod之间的关系可以这么理解:

    一个类(Class)持有一系列的方法(Method),在load类时,runtime会将所有方法的选择器(SELhash后映射到一个集合(NSSet)中(NSSet里的元素不能重复)。
    当需要发消息时,会根据选择器(SEL)去查找方法;找到之后,用Method结构体里的函数指针(IMP)去调用方法。这样在运行时查找selecter的速度就会非常快。


参考:

官网:selectorSELIMPMethod源码

Selector, Method 和 IMP 的区别与联系

OC 中 objc_selector 结构体详解

Method,SEL,Imp

深入探究SEL,Method,IMP

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫小言mo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值