美文网首页
OC方法交换的优雅实现

OC方法交换的优雅实现

作者: 梨花树下好乘凉 | 来源:发表于2021-07-27 22:51 被阅读0次

前言

刚才翻代码时发现N年前写的方法交换,当时方法交换还是个新奇的东东,网上找了一番发现都有各种问题,于是动手写了一个。如今方法交换的写法已经烂大街了,但把我当时写的拿出来一对比,不得不自吹一句,还是我自己写的更优雅。

上代码

我当年的实现代码

static void exchangeSelector(Class oClass, SEL oSelector, Class sClass, SEL sSelector) {
    
    Method originalMethod = class_getInstanceMethod(oClass, oSelector);
    Method swizzledMethod = class_getInstanceMethod(sClass, sSelector);
    
    IMP oIMP = method_getImplementation(originalMethod);
    IMP sIMP = method_getImplementation(swizzledMethod);
    
    const char *oType = method_getTypeEncoding(originalMethod);
    const char *sType = method_getTypeEncoding(swizzledMethod);
    
    class_replaceMethod(oClass, oSelector, sIMP, sType);
    class_replaceMethod(oClass, sSelector, oIMP, oType);
    
}

与某流行的代码对比

放在一起看,单论颜值就不是一个档次的

16274538549564.jpg

分析

目的

我们使用方法交换的目的实际是修改方法,当然还要保持原方法的实现可被利用。
如图:新方法的实现和原方法交换后,通过新方法名就可以使用原本的实现。

16273935303846.jpg

问题

而出现的BUG,通常是原方法的实现并不在类本身,而是在父类中,替换的时候影响到了父类

16273940647544.jpg

我们想要的结果

16274438965756.jpg

处理过程

理论处理

大象装冰箱分几步?1.获取大象,2.放进冰箱。
我们如何交换变量呢?1.获取旧值,2.放进新变量。下面的写法最容易理解了吧。

{
    //交换a, b两个变量的值;
    tmp1 = a;
    tmp2 = b;
    a = tmp2;
    b = tmp1;
}

接下来是交换方法,我们如何交换方法呢?1.获取旧实现,2.放进新方法。
把class比喻成字典,把方法名比喻成key,把方法实现比喻成value,把class有父类比喻字典也像NSUserDefaults一样有更深的域,那么我们要做的事情就是这个

exchangeSelector(funA, funB) {
    class = self.class;
    methodA = class[funA];
    methodB = class[funB];
    class[funA] = methodB;
    class[funB] = methodA;
}

再复杂点,methodB 没必要限定在本类,毕竟要加一堆类别也挺烦的,而且有些类是隐藏的,强行声明出来再加类别扩展方法总感觉很不靠谱。于是我们指定 methodB ,由于methodB的获取不是常用方法,我们换成把获取 methodB 所需的条件传入

exchangeSelector(class, funA, classForMethodB, funForMethodB) {
    methodA = class[funA];
    methodB = classForMethodB[funForMethodB];
    class[funA] = methodB;
    class[funB] = methodA;
}

我当年的处理

就是按上面最单纯的过程,换成最直白的代码。

  1. 获取实现
16273944303240.jpg
    //原实现
    Method originalMethod = class_getInstanceMethod(oClass, oSelector);
    IMP oIMP = method_getImplementation(originalMethod);
    const char *oType = method_getTypeEncoding(originalMethod);
    
    //新实现
    Method swizzledMethod = class_getInstanceMethod(sClass, sSelector);
    IMP sIMP = method_getImplementation(swizzledMethod);
    const char *sType = method_getTypeEncoding(swizzledMethod);
  1. 把实现放进方法。
16273945396107.jpg
    //把新实现放进原方法
    class_replaceMethod(oClass, oSelector, sIMP, sType);
    //把原实现放进新方法。这里要注意,我们的目的只是替换原本类里的方法。
    class_replaceMethod(oClass, sSelector, oIMP, oType);

什么?你问上面那个BUG的问题,原方法子类里没实现,是在父类实现的怎么办?

如果让你给一个变量赋值,你是这样做

int a;  //假如有一个变量a,我们要给它赋值
if (a) {    //先判断a原本是否有值
    a = 1;  //如果a已经有值了,就将a的旧值改成新值
} else {
    a = 1;  //如果a没值,则直接使用新值
}

还是这样做

int a;  //假如有一个变量a,我们要给它赋值
a = 1;  //不判断a当前是否有值,直接赋值

我选择后者。

如果你来设计一个函数,用来设置字典中某个key所对应的值,你会设计成当别人使用时,需要像左边这样先判断当前是否有值,再根据不同的情况进行不同的处理,还是向右边这样一步搞定?

16273964339426.jpg

我还是会选择后者,我问过别人,非常巧合的是他们也选择后者,更巧合的是 class_replaceMethod 也和我们一样选择了后者。所以我们根本不用考虑子类里有没有实现这种事情。

某流行处理

不多说了,除了 class_replaceMethod 还要了解 class_addMethod、method_exchangeImplementations ,然后再做判断,不同分支做不同处理,看起来就容易令人迷惑的样子。

相关文章

  • OC方法交换的优雅实现

    前言 刚才翻代码时发现N年前写的方法交换,当时方法交换还是个新奇的东东,网上找了一番发现都有各种问题,于是动手写了...

  • ios-Runtime(运行时)

    利用runtime来实现归档解档 方法交换 俗称 OC的方法欺骗 KVO的实现原理 用runtime来实现KVO...

  • 有了这些ios面试技巧,公司任你进

    利用runtime 可以做一些OC不容易实现的功能 1,动态交换两个方法的实现(特别是交换系统自带的方法) 2,动...

  • ios-黑魔法之方法交换MethodSwizzling

    原理: 方法交换是基于OC的动态绑定技术实现的,动态绑定简单的说就是我们OC对象直到运行时才把这个对象所具有的方法...

  • OC交换方法

    title: OC交换方法date: 2017-08-27tags: hook,method swizzling,...

  • OC runtime 底层API解析

    关于class的API 交换方法API 替换方法的实现 交换方法的实现

  • Swift严格的单例写法

    相比OC,Swift有很优雅的实现单例的写法。 实现 单例类Tools 客户端调用: 说明 当尝试使用 这种方法去...

  • OC中swizzling的“移魂大法”

    前言 OC的runtime机制是一个很有意思的东西,今天就来讲讲OC方法实现的交换,当然称之为“移魂大法”确实有点...

  • RunTime实现

    1:RunTiem交换方法实现 //runTime交换方法实现 // 1,创建已有类的分类,并且实现自己的方...

  • ios-面试-runtime中黑魔法方法交换

    方法交换-原理 方法交换,传言中的runtime中的黑魔法! 依据runtime的机制,OC中类生成的对象在运行时...

网友评论

      本文标题:OC方法交换的优雅实现

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