美文网首页iOS 开发者的日常iOS Developer开发进阶 Runtime
深入浅出Objective-C 运行时黑魔法 method sw

深入浅出Objective-C 运行时黑魔法 method sw

作者: KenZhangCn | 来源:发表于2017-01-12 15:28 被阅读62次

我们经常会浏览到关于method swizzling的文章, 都称之为黑魔法. 但是网络上各路大神的文章都很深奥, 本文我就以自己的理解深入浅出的解析一下这个黑魔法, 力求看了都能理解这个所谓的 "黑魔法"究竟是什么.


最近公司的项目遇到一个需求, 要在每一个viewWillDisappear时做一个操作, 调用一句代码. 看到这个需求时我就想起了之前遇过的黑魔法method swizzling;

  • 作用:
    首先先看这个method swizzling是做什么用的? method swizzling能调换方法名和方法实现之间的对应关系. 对于我的需求来说, 就是每次在Controller里调用viewWillDisappear的时候, 会执行另一个方法的实现, 在这里称作XXX_viewWillDisappear. 我调用的方法名是viewWillDisappear, 其实执行的是XXX_viewWillDisappear里面的实现, 然后我就可以在另一个实现里做我想做的事情.

  • 前提:
    Objective-C语言是一门动态语言, 一个方法在编译时并不确定, 只有在执行时才能确定. 消息分发之后才能确定执行者. 这个就是所谓的Runtime机制. 因为这种机制的存在, 必然存在一种消息和执行的对应关系, 即是一个Selector和一个IMP的对应关系. 而method swizzling的存在其实是调换了这种对应关系.

  • 操作:
    新建一个UIViewController的分类, 写一个XXX_viewWillDisappear方法, 这个方法是用来和系统的XXX_viewWillDisappear方法进行交换的. 然后重写+load方法, 在+load方法内部调换方法名与方法实现的对应关系.

  • +load+initialize:
    确保在方法调用之前viewWillDisappearXXX_viewWillDisappear的实现已经被调换, 因此在+load+initialize内部执行方法实现的调换都是可以的. 并且由于method swizzling是全局的交换, 所以应该在 dispatch_once 中完成.

  • 代码来源于NSHipster, 并对代码增加了注释.

@implementation UIViewController (swizzling)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(viewWillDisappear:);  //初始化SEL类型对象指向viewWillDisappear:
        SEL swizzledSelector = @selector(XXX_viewWillDisappear:); //初始化SEL类型对象指向XXX_viewWillDisappear:

        //viewWillDisappear:对应的Method, 为类方法列表中originalSelector指向的方法实现
        Method originalMethod = class_getInstanceMethod(class, originalSelector); 
        //XXX_viewWillDisappear:对应的Method, 为类方法列表中swizzledSelector指向的方法实现
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        /*
        在类中增加方法: 方法名为originalSelector(即viewWillDisappear:), 
        对应的方法实现为swizzledMethod的实现(即XXX_viewWillDisappear:对应的实现)
        */
        BOOL didAddMethod =
            class_addMethod(class,
                originalSelector,
                method_getImplementation(swizzledMethod),
                method_getTypeEncoding(swizzledMethod));
        
        if (didAddMethod) {
        /*
        替换类中方法实现: 方法名为swizzledSelector(即XXX_viewWillDisappear:), 
        替换为为originalMethod的实现(即viewWillDisappear:对应的实现)
        */ 
            class_replaceMethod(class,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod));
        } else {
            //直接调换originalMethod和swizzledMethod方法的实现
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling

- (void)XXX_viewWillDisppear:(BOOL)animated { 
    //在VC中调用`viewWillDisppear`实际是调用了这里的实现
    [self XXX_viewWillDisppear:animated];  //这一句代码, 实际上是调用`viewWillDisppear`到实现
    NSLog(@"viewWillDisappear: %@", self);
}

@end

参考文章:
http://nshipster.cn/method-swizzling/
http://www.cocoachina.com/ios/20141031/10105.html
http://blog.jobbole.com/45963/
文章整理参考网络文章,如有错误,欢迎讨论指出。

相关文章

网友评论

    本文标题:深入浅出Objective-C 运行时黑魔法 method sw

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