可能你们一直看到有许多朋友在Runtime相关文章中介绍IMP指针的概念,那么IMP究竟有什么实际做用呢?让咱们先从一个函数看起来。
函数
Method Swizzlingspa
若是对Runtime有必定了解的话,必定据说过或者用过这个函数:指针
void method_exchangeImplementations(Method m1, Method m2)
它一般叫作method swizzling,算是ObjC的"黑魔法"了,做用就是在程序运行期间动态的给两个方法互换实现,好比有这样一种使用场景:调试
咱们的程序中有许多个ViewController,我想在对项目改动最小的状况下,在当每一个Controller执行完ViewDidLoad之后就在控制台把本身的名字打印出来,方便我去作调试或者了解项目结构。code
有许多朋友会这样说,让全部控制器都继承一个BaseController不就能够了吗?我在这里要解释一下这样作的缺点:假如你的项目里有许多Controller的话,你就须要把项目里凡是没有继承自BaseController的每一个Controller都作一次修改了,并且随意更改层级结构会发生意想不到的错误。orm
其实咱们的目的就是重写ViewDidLoad的方法,并在他的方法最后加上几句Log,因此咱们须要给UIViewController创建一个category,由于咱们知道,若是在Catagory中重写一个方法,就会覆盖它的原有方法实现,可是,这样作之后就没有办法调用系统原有的方法,由于在一个方法里调用本身的方法会是一个死循环。因此咱们的解决办法就是,另外写一个方法来和viewDidLoad“交换”,这样外部调用viewDidLoad就会调到新建的这个方法中,一样,咱们调用新建的方法就会调用到系统的viewDidLoad中了。继承
IMP指针编译器
其实,还有一种更加简单的方法可让咱们办到相同的目的,运用IMP指针,IMP就是Implementation的缩写,顾名思义,它是指向一个方法实现的指针,每个方法都有一个对应的IMP,因此,咱们能够直接调用方法的IMP指针,来避免方法调用死循环的问题。it
调用一个IMP的方式和调用普通C函数相同,好比:io
id returnObjc = someIMP(objc,SEL,params...);
不过若是你的项目没有作其余配置的话这样调用编译器是不会经过的,咱们来看一下先它的定义:
if !OBJC_OLD_DISPATCH_PROTOTYPES typedef void (*IMP)(void /* id, SEL, ... */ ); else typedef id (*IMP)(id, SEL, ...); endif
在默认状况下你的工程是打开这个配置的
这种状况下IMP被定义为无参数无返回值的函数。因此你须要到工程里搜索到这个选项并把它关闭。这样的麻烦就是,每次使用,你都须要修改工程配置,因此这里我再介绍另一种办法:从新定义一个和有参数的IMP指针相同的指针类型,在获取IMP时把它强转为此类型。这样运用IMP指针后,就不须要额外的给ViewController写新的方法:
还有一个地方咱们须要注意,若是这样直接调用IMP的话就会发生经典的EXC_BAD_ACCESS错误,咱们定义的IMP指针是一个有返回值的类型,而其实咱们获取的viewDidLoad这个方法是没有返回值的,因此咱们须要新定义一个和IMP相同类型的函数指针好比VIMP,把他的返回值定位Void,这样若是你修改的方法有返回值就用IMP,没有返回值就用VIMP。
值得注意的是,若是你重写的方法有返回值,不要忘记在最后作return。
总结
实际上直接调用一个方法的IMP指针的效率是高于调用方法自己的,因此,若是你有一个合适的时机获取到方法的IMP的话,你能够试着调用它。
这是只是IMP使用的场景之一,它还有许多做用,但愿你们多多发现。