iOS使用 class_addMethod hook 类的方法

方案一

method_exchangeImplementations

直接使用method_exchangeImplementations
交换两个方法的实现。
该方案可以实现方法的替换,但是有风险,
就是如果是在子类实现了交换方法,但是交换的是一个父类实现了子类没有实现的方法,就会将父类方法的替换成子类的实现,如果父类的实例对象调用父类的方法的时候,由于已经替换成子类实现的方法,就会因为父类没有实现该方法导致崩溃

@interface Father : NSObject

-(void)easeapi;

@end

@implementation Father
-(void)easeapi {
    NSLog(@"father easeapi 方法");
    //your code
}
@end

@interface Son2 : Father

@end

#import "Son2.h"
#import <objc/runtime.h>

@implementation Son2

{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL fromSelector = @selector(easeapi);
        SEL toSelector = @selector(new_easeapi);

        Method fromMethod = class_getInstanceMethod(class, fromSelector);
        Method toMethod = class_getInstanceMethod(class, toSelector);
        method_exchangeImplementations(fromMethod, toMethod);
    });
}

-(void)new_easeapi {
    [self new_easeapi];
    NSLog(@"new_easeapi方法");
}

@end

报错如图
请添加图片描述

由此可见,如果 method_exchangeImplementations 交换的两个方法子类有一个子类没有实现的时候,就会替换父类的方法,风险就此而产生
由此我们可以使用方案二

方案二

代码

#import "Son2.h"
#import <objc/runtime.h>

@implementation Son2

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL fromSelector = @selector(easeapi);
        SEL toSelector = @selector(new_easeapi);

        Method fromMethod = class_getInstanceMethod(class, fromSelector);
        Method toMethod = class_getInstanceMethod(class, toSelector);

        if(class_addMethod(class, fromSelector, method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
            class_replaceMethod(class, toSelector, method_getImplementation(fromMethod), method_getTypeEncoding(fromMethod));
        } else {
            method_exchangeImplementations(fromMethod, toMethod);
        }
    });
}

-(void)new_easeapi {
    [self new_easeapi];
    NSLog(@"new_easeapi方法");
}

@end

这里使用了 class_addMethod ,class_replaceMethod 和 method_exchangeImplementations 共同完成方法替换

class_addMethod

给指定Class添加一个SEL的实现(或者说是SEL和指定IMP的绑定),添加成功返回YES,SEL已经存在或添加失败返回NO。
它有两个需要注意的点:

如果该SEL在父类中有实现,则会添加一个覆盖父类的方法;
如果该Class中已经有SEL,则返回NO。

执行class_addMethod能避免干扰到父类,这也是为什么推荐大家尽量先使用class_addMethod的原因

class_replaceMethod

如果该Class不存在指定SEL,则class_replaceMethod的作用就和class_addMethod一样;
如果该Class存在指定的SEL,则class_replaceMethod的作用就和method_setImplementation一样。
如图,没有影响到父类请添加图片描述

注意事项

如果 父类和子类都没有某个方法的SEL,我们在进行class_addMethod的时候会返回YES,
但是 class_replaceMethod 是失败的

@interface Father : NSObject

@end

@implementation Father

@end


@implementation Son2

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL fromSelector = @selector(easeapi);
        SEL toSelector = @selector(new_easeapi);

        Method fromMethod = class_getInstanceMethod(class, fromSelector);
        Method toMethod = class_getInstanceMethod(class, toSelector);

        if(class_addMethod(class, fromSelector, method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
            class_replaceMethod(class, toSelector, method_getImplementation(fromMethod), method_getTypeEncoding(fromMethod));
        } else {
            method_exchangeImplementations(fromMethod, toMethod);
        }
    });
}

-(void)new_easeapi {
    NSLog(@"new_easeapi方法");
}

调用 class_addMethod 添加的SEL
效果如图
请添加图片描述

调用原来存在的SEL
请添加图片描述
可以看到 执行的方法都是同一个实现,因为添加的SEL 没有实现,所以 class_addMethod 成功了,但是class_replaceMethod 是失败的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值