iOS 黑魔法 - Method Swizzling实现原理

1,633 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

Method Swizzling

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

objc_class

在Objective-C的Runtime中,一个类是用一个名为objc_class的 结构体表示的,它的定义如下:

objc_class结构体.png

objc_method_list

我们从上述结构体中可以发现一 个objc_method_list指针,它保存着当前类的所有方法列表。同 时,objc_method_list也是一个结构体,它的定义如下:

objc_method_list.png

objc_method

我们从上面的结构体中发现一个objc_method字段,它的定义如下: objc_method字段.png 我们从上面的结构体中还发现,一个方法由如下三部分组成。

  • method_name:方法名。
  • method_types:方法类型。
  • method_imp:方法实现。

交换原理

使用Method Swizzling交换方法,其实就是修改了objc_method 结构体中的mthod_imp,即改变了method_name和method_imp的映射关系如下: 方法交换示意图.png

实现Method Swizzling的相关函数

Method class_getInstanceMethod(Class_Nullable aClass,SEL_Nonnull aSelector) OBJC_AVAILABLE(10.0,2.0,9.0,1.0,2.0);

  • 功能:返回目标类aClass、方法名为aSelector 的实例方法。
  • 参数说明
    • aClass:目标类。
    • aSelector:方法名。 注意点只有当前类以及它的父类均不包含指定 的方法时,才会返回NULL。

BOOL class_addMethod(Class_Nullable aClass,SEL_Nonnull aSelector,IMP_Nonnullimp,const char * _Nullable types) OBJC_AVAILABLE(10.5,2.0,9.0,1.0,2.0);

  • 功能:给目标类aClass添加一个新的方法, 同时包括方法的实现。

  • 参数说明

    • aClass:目标类。
    • aSelector:要添加方法的方法名。
    • imp:要添加方法的方法实现。
    • types:方法实现的编码类型。

注意点 返回成功,说明已经成功在目标类中添 加了该方法;返回失败,说明在目标类中已经存 在相同方法名的方法。

IMP method_getImplementation(Method_Nonnull method) OBJC_AVAILABLE (10.5,2.0,9.0,1.0,2.0);

  • 功能:返回方法实现的指针。

  • 参数说明

    • method:目标方法。

IMP class_replaceMethod(Class_Nullable aClass,SEL_Nonnull aSelector,IMP_Nonnull imp,const char * _Nullable types) OBJC_AVAILABLE(10.5,2.0,9.0,1.0,2.0);

  • 功能:替换目标类aClass的aSelector方法的指 针。
  • 参数说明
    • aClass:目标类。
    • aSelector:目前方法的方法名。
    • imp:新方法的方法实现。
    • types:方法实现的编码类型。

void method_exchangeImplementations(Method_Nonnull m1,Method_Nonnull m2) OBJC_AVAILABLE(10.5,2.0,9.0,1.0,2.0);

  • 功能:交换两个方法的实现指针。
  • 参数说明
    • m1:交换方法1。
    • m2:交换方法2
#import <objc/runtime.h>

@implementation NSObject (Swizzler)


+ (BOOL)sensorsdata_swizzleMethod:(SEL)originalSEL withMethod:(SEL)alternateSEL {
   
    //获取原始的方法
    Method originalMethod = class_getInstanceMethod(self, originalSEL);
    if (!originalMethod) {
        return NO;
    }
    //获取将要交换的方法
    Method alternateMethod = class_getInstanceMethod(self, alternateSEL);
    if (!alternateMethod) {
        return NO;
    }
    //交互两个方法的实现
    method_exchangeImplementations(originalMethod, alternateMethod);
    //返回yes,方法交换成功
    return YES;
}

@end