需求
已知类MyClass具有私有方法methodB;要求如何在不改变MyClass源码的基础上扩展methodB方法,使其在执行methodB方法的时候具有新增功能myAction。
看着可能不太明白,这里举个例子:IQKeyboardManager
应该都不陌生,现在要求在点击Done
按钮的同时执行自定义事件myAction
。
分析源码发现’’Done”按钮对应的方法- (void)doneAction:(IQBarButtonItem*)barButton
在IQKeyboardManager.m中,对于这个私有方法貌似只能通过修改IQKeyboardManager.m源码来进行扩展了,但奈何项目是用CocoaPods进行管理的,如果直接修改三方库源码也就意味着IQKeyboardManager
需要从CocoaPods管理中移除,这对于有强迫症的人来说自然是不能忍的😣😣
怎么办
这时就轮到Runtime上场了,借助Runtime的Method Swizzling(方法替换)以及Associated Object(关联对象),可以完美的实现以上需求,同时还可以对相关类进行扩展。
假设类MyClass
如下:
MyClass.h
文件
1 |
|
MyClass.m
文件
1 |
|
MyClass
类的共有方法:-methodA
以及-test
,而调用test其实会执行私有方法-methodB
要在不改变源码的基础上对- methodA
以及- methodB
方法进行扩展,那么可以新建类别MyClass+MyCategory
,并在initialize
方法中将- methodA
与- methodB
方法替换为自定义方法,其中initialize
会在第一次初始化这个类之前被调用,如果始终没用到则不会调用,有点类似于懒加载。
另外在MyMethodA
与MyMethodB
中调用了自身,那是不是会引起死循环呢?答案是不会的,因为使用method_exchangeImplementations进行了方法替换,在调用MyMethodA
时实现的是methodA
方法。
1 |
|
#import "MyClass+MyCategory.h"
,再次运行程序,可以看到
到这一步,我们已经可以对MyClass类的方法进行自定义扩展,你可以在自定义方法- MyMethodA
、 - MyMethodB
中通过NSNotification或直接调用其他类的方法来触发具体的需求方法了。但这样做的话会使得MyClass
类与其它类的耦合性太强,而且也不方便调试。在这里我们应该设计相关接口,以供外部调用实现它们自定义的各种需求方法。
那么继续往下看:
1 |
|
给MyClass+MyCategory
类别设计两个方法,用来给第三方传递目标方法以及移除目标方法。
1 |
|
在.m文件的实现中,使用Associated Object,MyClass
内部扩展了相应属性,并在恰当的时候触发传递进来的自定义方法。
再次运行:
可以看到,在运行MyClass
的- methodA
与- methodB
方法时,正确触发了自定义方法,而且我们还可以给自定义方法传递参数。需求搞定!!
写在最后
以上便是 Runtime 在实际开发中的一些运用,其实还可以更加灵活,对于那些就算是无法看到源代码的三方库,也可以运用runtime拿到其方法列表,然后通过分析,在对应的地方替换或注入方法,以达到你想要的效果。比如前段时间很火的微信自动抢红包,就是运用了此种技术,有兴趣的可以研究下,这里就不做讨论了。