美文网首页iOS学习开发iOS Developer
OC 黑魔法之 Method Swizzling

OC 黑魔法之 Method Swizzling

作者: Jerry_Lee | 来源:发表于2017-06-03 13:31 被阅读102次

    基础实现

    Method swizzling(有些人也叫它方法交换)对绝大多数OC开发者来说都是很熟悉但并没有太多应用场景的一个东西。但是了解它对于了解OC底层runtime有很好的帮助。而且可以绕过一个限制,实现某些特殊的功能。
    使用Method swizzling,写法是基本相同的。比如说要给UIApplicationsendEvent:方法做交换,创建一个UIApplication的分类,在load方法中实现。

    Method sendEventMethod = class_getInstanceMethod([self class], @selector(sendEvent:));
        Method sendNotifyEventMethod = class_getInstanceMethod([self class], @selector(my_sendEvent:));
        
        method_exchangeImplementations(sendEventMethod, sendNotifyEventMethod);
    

    然后再写一个你想用来替换系统sendEvent:实现的方法,在这个例子中叫做my_sendEvent:,然后再这个实现中调用自身。

    - (void)my_sendEvent:(UIEvent *)event {
        
        NSLog(@"send evnet before %@",event);
        
        [self my_sendEvent:event];
        
        NSLog(@"send event after %@",event);
    }
    
    

    按照日常写代码的经验,在方法中调用自己,这不写成死循环了吗。但在这里,却并不会形成循环。这是因为我们在上面调用了method_exchangeImplementations方法,将方法名和方法的实现进行了交换,当系统调用sendEvent:方法时,调用到了my_sendEvent:的实现中,这时候我就可以做我想要的处理,并在这个方法中调用my_sendEvent:方法,但其实调用的实现是系统的sendEvent:,这样可以在完成系统实现的基础上完成了自己想要的逻辑。

    • 在这个方法中打断点也可以看到_cmdsendEvnet:而不是my_sendEvent:

    特殊写法

    前几日在看到AFNetworking源码的时候突然想起之前看的某篇博客说AFNetworkingdataTaskresume方法好像也是经过方法交换的,想看一下它是怎么做的,是否是和我上面写的一样。于是就搜索了了一下method_exchangeImplementations方法,发现AFN并没有通过分类实现,于是我就看了下它的实现方法。

    AFN的源码看起来比较绕,但剥离方法的封装之后,看起来就简单多了。比如想要交换上述的sendEvent:方法,并不需要写在分类中,而是随便写了一个类,在这个类内部处理。

    + (void)load {
        
        Method mySendEvent = class_getInstanceMethod([self class], @selector(my_sendEvent:));
        if(class_addMethod([UIApplication class], @selector(my_sendEvent:), method_getImplementation(mySendEvent), method_getTypeEncoding(mySendEvent))) {
         
            Method originalMethod = class_getInstanceMethod([UIApplication class], @selector(sendEvent:));
            Method swizzlingMethod = class_getInstanceMethod([UIApplication class], @selector(my_sendEvent:));
            method_exchangeImplementations(originalMethod, swizzlingMethod);   
        }    
    }
    
    - (void)my_sendEvent:(UIEvent *)event {
        
        [self my_sendEvent:event];
    }
    

    与之前的写法大致相同,唯一不同的是,将自己的实现方法my_sendEvent:写在了这个类中,并将这个方法用class_addMethod添加给了UIApplication。在自己的方法中打断点可以发现,这里的self并不是这个类的对象,而是UIApplication的对象。这个涉及到了OC底层objc_msgSend方法的原理,在这里就不细说了。

    相关文章

      网友评论

        本文标题: OC 黑魔法之 Method Swizzling

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