SwizzleMethod的黑魔法

作者: 秋刀生鱼片 | 来源:发表于2015-12-15 11:21 被阅读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方法中,可以给这个方法新建一个类,每次新建工程直接拖进来一起编译,分分钟植入,帅爆一切,点个赞吧。

    相关文章

      网友评论

      本文标题:SwizzleMethod的黑魔法

      本文链接:https://www.haomeiwen.com/subject/sjudhttx.html