美文网首页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