在Objective-C运行时替换类中的方法

2014-04-26 Xiaosong Gao 更多博文 » 博客 » GitHub »

iOS Mac

原文链接 https://gaoxiaosong.github.io/2014/04/26/replace-class-method.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


我们可以在Objective-C运行时将一个类中的一个方法,动态的替换为该类中的另一个方法。并且在替换后的方法中,依然可以调用以前的方法。

#import <objc/objc-class.h>
void MethodSwizzle(Class aClass, SEL orig_sel, SEL alt_sel)
{
  Method orig_method = nil, alt_method = nil;
  // First, look for the methods
  orig_method = class_getInstanceMethod(aClass, orig_sel);
  alt_method = class_getInstanceMethod(aClass, alt_sel);
  // If both are found, swizzle them
  if ((orig_method != nil) && (alt_method != nil)) {
    method_exchangeImplementations(orig_method, alt_method);
  }
}

这个替换过程,实际上是将两个方法的实现进行了交换,也就是说原来methodA的方法名字对应methodA的方法实现,但是现在methodA的方法名字对应methodB的方法实现,而methodB的方法名字却对应methodA的方法实现。

iOS和Mac有很多私有的类和函数,令人忧伤的是这些官方都不给用,对于一个开发者来说这是相当纠结的一件事。但如果你知道这个私有方法怎么实现的话呢?完全可以自己改写这些私有类和方法,下面以改写Xcode中的IDEWorkspace这个类的一些方法作为例子来说明一下。

@interface IDEWorkspace (KSImageNamed)
@end

@implementation IDEWorkspace (KSImageNamed)

+ (void)load
{
  MethodSwizzle(self, @selector(_updateIndexableFiles:), @selector(swizzle__updateIndexableFiles:));
}

- (void)swizzle__updateIndexableFiles:(id)arg1
{
  [self swizzle__updateIndexableFiles:arg1];
  // 自定义代码
}

@end

这里我们在load中对于方法进行交换,此时updateIndexableFiles:方法名对应于swizzleupdateIndexableFiles:的方法实现,而swizzleupdateIndexableFiles:方法名却对应于_updateIndexableFiles:的方法实现,故在新的方法中调用[self swizzle_updateIndexableFiles:arg1];实际上进行的是方法名却对应于_updateIndexableFiles:的方法实现过程。