• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

iOS-Runtime:SEL、IMP、Method

武飞扬头像
MinggeQingchun
帮助1

SEL、IMP、Method之间的关系:一个类(Class)持有一个分发表,在运行期分发消息,表中的每一个实体代表一个方法(Method),它的名字叫做选择子(SEL),对应着一种方法实现(IMP)。具体的分析如下:

参考苹果官方公开源码

objc4源码在线浏览

objc4源码下载

1、SEL

在上述苹果官网公开源码objc4的objc.h文件中,定义如下:

  1.  
    /// An opaque type that represents a method selector.
  2.  
    typedef struct objc_selector *SEL;

SEL方法选择器(翻译成中文叫做选择子或者选择器),表示一个selector的指针,代表方法的名称,仅以名字来识别。

SEL代表方法在Runtime期间的标识符。为SEL类型,虽然SEL是objc_selector结构体指针,但实际上它只是一个C字符串。

在类加载的时候,编译器会生成与方法相对应的选择子,并注册到Objective-C的Runtime运行系统。
无论什么类里,无论类是否存在依存关系,只要方法名相同,SEL就相同。

项目里的所有SEL都保存在一个NSSet集合里(NSSet集合里的元素不能重复),所以查找对应方法,只要找到对应的SEL就可以了。
SEL实际是根据方法名hash化了的字符串

  1.  
    SEL sel_registerName(const char *str)//向runtime system注册一个方法名。如果方法名已经注册,则放回已经注册的SEL
  2.  
    SEL sel_getUid(const char *str)//同上
  3.  
    @selector(<#selector#>)//oc编译器提供的
  4.  
    SEL NSSelectorFromString(NSString *aSelectorName)//OC字符串转化
  5.  
    SEL method_getName ( Method m );//根据Method结构体获取

SEL的操作函数

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

虽然SEL是方法的唯一标识,但不同的类调用名字相同的方法应该如何处理,接下来就要用到IMP

2、IMP

代码定义:

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

可以简化为:

  1.  
    /// A pointer to the function of a method implementation.
  2.  
    typedef id (*IMP)(id, SEL , ...);

IMP代表函数指针,指向方法实现的首地址,即函数执行的入口。

该函数使用标准的C调用。

第一个参数指向self(它代表当前类实例的地址,如果是类则指向的是它的元类),作为消息的接受者;

第二个参数代表方法的选择子;...代表可选参数,前面的id代表返回值。
那么,XX调用了XXX方法,其参数为XX都确定下来了。

IMP的高级作用

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

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

获取一个方法的IMP时候可以直接调用IMP

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

3、Method

runtime.h文件中,代码定义如下:

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

objc_method结构体定义如下:

  1.  
    /// Method
  2.  
    struct objc_method {
  3.  
    SEL method_name OBJC2_UNAVAILABLE;//方法名
  4.  
    char *method_types OBJC2_UNAVAILABLE;//参数返回值字符串描述
  5.  
    IMP method_imp OBJC2_UNAVAILABLE;//方法的实现
  6.  
    }

MethodMethod是一个objc_method结构体指针,该结构体中包含一个SEL和IMP。

实际上相当于在SEL和IMP之间作了一个映射。有了Method,SEL就可以找到对应的IMP,从而调用方法。对开发者来说是一种不透明的类型,被隐藏在我们平时书写的类或对象的方法背后。

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

Method操作函数如下:

  1.  
    方法操作主要有以下函数:
  2.  
    // 添加方法
  3.  
    BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
  4.  
     
  5.  
    // 获取实例方法
  6.  
    Method class_getInstanceMethod ( Class cls, SEL name );
  7.  
     
  8.  
    // 获取类方法
  9.  
    Method class_getClassMethod ( Class cls, SEL name );
  10.  
     
  11.  
    // 获取所有方法的数组
  12.  
    Method * class_copyMethodList ( Class cls, unsigned int *outCount );
  13.  
     
  14.  
    // 替代方法的实现
  15.  
    IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
  16.  
     
  17.  
    // 返回方法的具体实现
  18.  
    IMP class_getMethodImplementation ( Class cls, SEL name );
  19.  
    IMP class_getMethodImplementation_stret ( Class cls, SEL name );
  20.  
     
  21.  
    // 类实例是否响应指定的selector
  22.  
    BOOL class_respondsToSelector ( Class cls, SEL sel );
学新通

class_addMethod的实现会覆盖父类的方法实现,但不会取代本类中已存在的实现,如果本类中包含一个同名的实现,则函数会返回NO。如果要修改已存在实现,可以使用method_setImplementation 一个Objective-C方法是一个简单的C函数,它至少包含两个参数–self和_cmd。所以,我们的实现函数(IMP参数指向的函数)至少需要两个参数

class_getInstanceMethodclass_getClassMethod函数,与class_copyMethodList不同的是,这两个函数都会去搜索父类的实现。

class_copyMethodList函数,返回包含所有实例方法的数组,如果需要获取类方法,则可以使用class_copyMethodList(object_getClass(cls), &count)(一个类的实例方法是定义在元类里面)。该列表不包含父类实现的方法。outCount参数返回方法的个数。在获取到列表后,我们需要使用free()方法来释放它。

class_replaceMethod函数,该函数的行为可以分为两种:如果类中不存在name指定的方法,则类似于class_addMethod函数一样会添加方法;如果类中已存在name指定的方法,则类似于method_setImplementation一样替代原方法的实现。

class_getMethodImplementation函数,该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。例如,如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分。

class_respondsToSelector函数,我们通常使用NSObject类的respondsToSelector:或instancesRespondToSelector:方法来达到相同目的。

参考文章

SEL、Method和IMP区别

深入探究SEL,Method,IMP

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgabakk
系列文章
更多 icon
同类精品
更多 icon
继续加载