iOS之runtimeCocoa 框架iOS Developer - Runtime

SwizzleMethod的黑魔法

2015-12-15  本文已影响594人  秋刀生鱼片
    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方法,因为这个这个方法指针实际已经指向AppDelegateapplication:didFinishLaunchingWithOptions方法。其他地方掉用AppDelegateapplication:didFinishLaunchingWithOptions方法则会指向我们的swizzle_application:application方法,这样我们就在人不知不觉中,向AppDelegate插入了一段代码,这一切不需要AppDelegate引入任何头文件,是不是很Cool?

这样一来就可以把需要放在这里面的各种监测代码初始化,都放到我们的swizzle_application:application方法中,可以给这个方法新建一个类,每次新建工程直接拖进来一起编译,分分钟植入,帅爆一切,点个赞吧。

上一篇下一篇

猜你喜欢

热点阅读