iOS IMP

作者: 萨缪 | 来源:发表于2020-04-27 22:34 被阅读0次

一.什么是IMP

IMP是”implementation”的缩写,它是objetive-C 方法(method)实现代码块的地址,可像C函数一样直接调用。通常情况下我们是通过[object method:parameter]或objc_msgSend()的方式向对象发送消息,然后Objective-C运行时(Objective-C runtime)寻找匹配此消息的IMP,然后调用它;但有些时候我们希望获取到IMP进行直接调用。
二.Objetive-C中的Method结构

在Objecitve-C中,在类中对每一个方法有一个在运行时构建的数据结构,在Objective-C 2.0中,此结构对用户不可见,但仍在内部存在。

struct objc_method
{
  SEL method_name;
  char * method_types;
  IMP method_imp;
};
typedef objc_method Method;

每个方法有3个属性

方法名:方法名为此方法的签名,有着相同函数名和参数名的方法有着相同的方法名。

方法类型:方法类型描述了参数的类型。

IMP: IMP即函数指针,为方法具体实现代码块的地址,可像普通C函数调用一样使用IMP。

由于Method的内部结构不可见,所以不能通过method->method_name的方式访问其内部属性,只能Objective-C运行时提供的函数获取。

SEL method_getName(Method method);
IMP method_getImplementation(Method method);
const char * ivar_getTypeEncoding(Ivar ivar);

三.获取当前方法的默认IMP(default IMP)

NSObject对象提供了两个方法来获取的IMP

- (IMP)methodForSelector:(SEL)aSelector;
+ (IMP)instanceMethodForSelector:(SEL)aSelector;

使用methodForSelector方法时,若向类(class)发送消息,则aSelector应该是类方法(class method);若向实例对象(instance)发送消息,则aSelector应该为实例对象方法(instance method)。使用instanceMethodForSelector可向类请求实例方法的IMP。
methodForSelector返回的IMP是default IMP,即发送消息时会调用的IMP。

那么获取当前方法的IMP,可使用self对象和隐含的_cmd参数。
隐含参数:尽管这些参数不是显式声明的,源码仍能引用它们(正像它能引用接收对象的实例变量一样)。
_cmd 是隐藏的参数,表示当前方法的selector
作用:

1、获取当前被调用方法:NSStringFromSelector(_cmd)

2、在运行时时使用:在某个分类方法里为对象动态添加属性,由于_cmd是在编译时候(compile - time)就可以确定的值,因此可以直接使用

IMP current = [[self class] instanceMethodForSelector:_cmd];

四. 另一种hack的方式获取当前方法IMP

GCC有个内建(build-in)函数,可获取当前栈帧的返回地址(参数0表示当前栈帧)。

__builtin_return_address(0)

这里有两个假设

方法的实现是连续的。

在方法中调用子函数,在子函数中调用__builtin_return_address(0)得到的返回地址在当前方法实现的内部。

那么可以得出结论,当前方法实现的起始地址(即IMP)肯定在的子函数返回地址的前面,而且会比任何其他方法离的更近。则可通过如下方法寻找IMP。

当当前方法中,调用一个寻找IMP的子函数,将当前类和_cmd参数传递给子函数;

在子函数中使用__builtin_return_address(0)获得返回地址,遍历当前类和当前父类的所有方法,寻找method_name与_cmd相等,而method_imp在__builtin_return_address(0)之前,且离其最近,则此IMP即为当前的的IMP。

import <objc/objc-class.h>

IMP impOfCallingMethod(id lookupObject, SEL selector)
{
NSUInteger returnAddress = (NSUInteger)__builtin_return_address(0);
NSUInteger closest = 0;

// 循环访问类和所有超类
Class currentClass = object_getClass(lookupObject);
while (currentClass)
{
    //  遍历此类的所有实例方法
    unsigned int methodCount;
    Method *methodList = class_copyMethodList(currentClass, &methodCount);
    unsigned int i;
    for (i = 0; i < methodCount; i++)
    {
        //  忽略具有不同选择器的方法
        if (method_getName(methodList[i]) != selector)
        {
            continue;
        }

        //  如果此地址更近,请使用它
        NSUInteger address = (NSUInteger)method_getImplementation(methodList[i]);
        if (address < returnAddress && address > closest)
        {
            closest = address;
        }
    }

    free(methodList);
    currentClass = class_getSuperclass(currentClass);
}

return (IMP)closest;

}

其使用方法为:

- (void)myMethodWithParam1:(int)someParameter andParam2:(int)otherParameter
{
   IMP myImplementation = impOfCallingInstanceMethod([self class], _cmd);
 
   ...
}

另外还有更快速的方法:获取当前方法的返回地址,然后通过此地址回溯到父函数的IMP内部,在父函数IMP中寻找调用当前函数的位置,即可知道当前函数的IMP,不过此方法是平台相关的,就不详细介绍。

相关文章

  • iOS runtime通过selector获取IMP地址

    iOS runtime通过selector获取IMP地址 获取IMP地址有两种方法: class_getMetho...

  • iOS IMP

    IMP是”implementation”的缩写,存储的是OC实现方法代码块的地址。一般通过[obj method:...

  • iOS IMP

    一.什么是IMP IMP是”implementation”的缩写,它是objetive-C 方法(method)实...

  • 自定义tabbar

    关于iOS自定义UITabBar的几种方法 #import "CDTabBarController.h" #imp...

  • iOS IMP和SEL

    SEL : 类成员方法的指针,但不同于C语言中的函数指针,函数指针直接保存了方法的地址,但SEL只是方法编号。 I...

  • iOS IMP 与 SEL

    IMP 官方文档 SEL 官方文档 sel_registerName 通过SEL 找到对应的IMP并调用 meth...

  • iOS【IMP 与 SEL】

    摘录:ChinaChong FunctionNSSelectorFromStringReturns the sel...

  • iOS 消息转发机制

    上节(iOS 消息查找流程)我们讲到,在iOS中对象调用方法,会经历方法的查找,如果查找到方法的IMP,那么就返回...

  • iOS进阶之Runtime----方法---type

    iOS进阶之Runtime----方法---type Type:方法的签名 SEL:方法的名称 IMP:方法的指针...

  • iOS 正则表达式判断手机号,邮箱等

    iOS正则表达式判断手机号,邮箱等 #import "NSString+RegexCategory.h" @imp...

网友评论

      本文标题:iOS IMP

      本文链接:https://www.haomeiwen.com/subject/wvppwhtx.html