void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
{
// the method might not exist in the class, but in its superclass
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// class_addMethod will fail if original method already exists
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
// the method doesn’t exist and we just added one
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
上面这个C风格函数,就是SwizzleMethod的核心方法,用来交换Runtime中类和对象的方法接口指针。但是这有什么用呢?
你知道有名的第三方库IQKeyboard么?
这个吊库,不需要引入头文件,不需要调用任何方法就能使用。怎么做到的呢?
答案是NSObject的 + (void)load
方法。
这个类方法,在软件运行时一定会调用一次,并且不需要调用super方法,因为父类的load方法也一定会调用。
IQKeyboard就是在load方法中初始化的。
SwizzleMethod应用实例 —— 无痛手术
这个比喻并不准确,准确说应该是无痕手术 —— 对方法的无痕手术
+ (void)load
{
swizzleMethod([AppDelegate class], @selector(application:didFinishLaunchingWithOptions:), @selector(swizzle_application:didFinishLaunchingWithOptions:));
}
这里,我们把AppDelegate的启动方法更换成了我们自己的swizzle_application:didFinishLaunchingWithOptions方法。两个方法指针互换,然后我们在我们的方法中加入我们需要的代码。
- (BOOL)swizzle_application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//我们需要添加的代码
//
return [self swizzle_application:application didFinishLaunchingWithOptions:launchOptions];
}
注意到了么,结尾我们自己调用swizzle_application:application
方法,因为这个这个方法指针实际已经指向AppDelegate
的application:didFinishLaunchingWithOptions
方法。其他地方掉用AppDelegate
的application:didFinishLaunchingWithOptions
方法则会指向我们的swizzle_application:application
方法,这样我们就在人不知不觉中,向AppDelegate
插入了一段代码,这一切不需要AppDelegate引入任何头文件,是不是很Cool?
这样一来就可以把需要放在这里面的各种监测代码初始化,都放到我们的swizzle_application:application
方法中,可以给这个方法新建一个类,每次新建工程直接拖进来一起编译,分分钟植入,帅爆一切,点个赞吧。