需求
已知类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拿到其方法列表,然后通过分析,在对应的地方替换或注入方法,以达到你想要的效果。比如前段时间很火的微信自动抢红包,就是运用了此种技术,有兴趣的可以研究下,这里就不做讨论了。