runtime 如何通过 selector 找到对应的 IMP 地址?

实例方法类方法的存储位置:

  • 实例方法:每个实例的isa指针指向着对应类对象,而每一个类对象中都一个对象方法列表。
  • 类方法:每个类对象的isa指针都指向着对应的元类对象,而每一个元类对象中都有一个类方法列表。
  • 当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类对象方法列表里查找。
  • 当我们发送一个消息给一个类时,这条消息会在类的meta class对象的方法列表里查找。
  • 方法列表中记录着一个个方法实体(结构体Method),实体中包含方法的名称,方法实现,以及参数类型。
  • 其实selector本质就是C字符串的方法名称(见下面分析),通过这个方法名称就可以在方法列表中找到方法实体,进一步找到对应的方法实现。

Selector,Method 和 IMP 的区别与联系

Selector
定义:

typedef struct objc_selector *SEL

翻译成中文叫做选择子或者选择器,选择子代表方法在Runtime期间的标识符。为SEL类型,虽然SELobjc_selector结构体指针,但实际上它只是一个C字符串。在类加载的时候,编译器会生成与方法相对应的选择子,并注册到Objective-CRuntime运行系统。

常见的有两种方式来获取/创建选择子:

SEL selA = @selector(setString:);
SEL selB = sel_registerName("setString:"); 

在控制台测试:

(lldb) p selA
(SEL) $1 = "setString:"
(lldb) p selB
(SEL) $2 = "setString:"

两者打印出来的都是字符串。

我们从sel_getName()方法的源码可以看出SELconst char *是可以相互转化的:

const char *sel_getName(SEL sel) {
   return sel ? (const char *)sel : "<null selector>";
}

如果将selAselB强转为为const char *

(lldb) p (const char *)$2
(const char *) $3 = 0x00007fff9a3794b5 "setString:"
(lldb) p (const char *)$1
(const char *) $4 = 0x00007fff9a3794b5 "setString:"
(lldb) 

上面的结果可以看出,selAselB指向相同的地址,代表同一个字符串。

如果某个类实现了setString:方法,那么以上两个选择子传入 respondsToSelector()方法返回的结果都将为YES

不同类中相同名字的方法所对应的方法选择子是相同的。

Implementation(IMP):
定义:

typedef id (*IMP)(id, SEL, ...)

代表函数指针,即函数执行的入口。该函数使用标准的C调用。

  • 第一个参数指向self(它代表当前类实例的地址,如果是类则指向的是它的元类),作为消息的接受者;
  • 第二个参数代表方法的选择子;
  • ...代表可选参数;
  • 前面的id代表返回值。

Method
定义:

typedef struct objc_method *Method

Method 对开发者来说是一种不透明的类型,它是一个objc_method结构体指针,objc_method的定义为:

/// An opaque type that represents a method in a class definition.
    typedef struct objc_method *Method;

    struct objc_method {
        SEL method_name;  // 方法选择器。
        char *method_types;  // 存储着方法的参数类型和返回值类型。
        IMP method_imp;  // 函数指针。
    } 
  • 方法名method_name类型为SEL,前面提到过相同名字的方法即使在不同类中定义,它们的方法选择器也相同;
  • 方法类method_types是个char指针,其实存储着方法的参数类型和返回值类型,即是Type Encoding编码;
  • method_imp指向方法的实现,本质上是一个函数的指针,就是前面讲到的Implementation

SelectorMethodIMP它们之间的关系可以这么解释:
一个类(Class)持有一个方法列表,在运行期分发消息,表中的每一个实体代表一个方法(Method),它的名字叫做选择子(SEL),对应着一种方法实现(IMP


iOS面试题汇总

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

推荐阅读更多精彩内容