iOS Runtime之方法替换

Runtime系列导读

  • iOS Runtime之方法查找
  • iOS Runtime之方法替换
  • iOS Runtime之KVO
  • iOS Runtime之KVC
  • iOS Runtime之反射调用

简介

Method Swizzling,顾名思义,就是交换两个 方法的实现。简单来说,就是利用Objective-C Runtime的动态绑定特性,将一个方法的实现与另 一个方法的实现进行交换。

分析

数据结构

  • objc_method
    • 我们从上面的结构体中发现一个objc_method字段,它的定义如下:


      image.png
  • 我们从上面的结构体中还发现,一个方法由如下三部分组成。
    • method_name:方法名。
    • method_types:方法类型。
    • method_imp:方法实现。

交换原理

  • 使用Method Swizzling交换方法,其实就是修改了objc_method 结构体中的mthod_imp,即改变了method_name和method_imp的映射关系如下:
image.png
  • 看一个例子
@implementation NSObject (Swizzler)

+ (void)swizzleInstanceMethodWithOriginalSEL:(SEL)originalSel SwizzleNewSEL:(SEL)newSel {
    
    Method originalMethod = class_getInstanceMethod(self, originalSel);
    Method newMethod = class_getInstanceMethod(self, newSel);
    if (!originalMethod || !newMethod) {
        return;
    }
    //加一层保护措施,如果添加成功,则表示该方法不存在于本类,而是存在于父类中,不能交换父类的方法,否则父类的对象调用该方法会crash;添加失败则表示本类存在该方法
    BOOL addMethod = class_addMethod(self, originalSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
    if (addMethod) {
        //再将原有的实现替换到swizzledMethod方法上,从而实现方法的交换,并且未影响到父类方法的实现
        class_replaceMethod(self, newSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }else{
        method_exchangeImplementations(originalMethod, newMethod);
    }
}

@end

答疑

如何hook一个对象的方法,而不影响其它对象

设置isa指向的ClassClass object_setClass(id obj, Class cls)

GXPerson* person = [GXPerson new];
[person test];
object_setClass(person, [GXPerson2 class]);
[person test];

class_replaceMethod和class_addMethod区别

  • class_replaceMethod是替换某个类的方法的实现,功能上可以替代class_addMethod, 但是class_addMethod只能在SEL没有IMP指向时才可以添加成功,而class_replaceMethod不管SEL 有没有`IMP实现,都可以添加成功

class_replaceMethod和method_exchangeImplementations的不同

  • 当使用class_replaceMethod替换父类的方法时,只有在子类里面调用才有效,实际上是在子类里面重写了父类的方法
  • method_exchangeImplementations 是交换方法的实现,当获取的方法来自于父类时,也会造成父类调用时被触发, 交换方法的实现,由于方法的实现来自于父类,所以实际是交换的父类的方法的实现

你可能感兴趣的:(iOS Runtime之方法替换)