拦截任意object-c类的函数

版权声明: https://blog.csdn.net/henysugar/article/details/84246976

一种有缺点的方法

想拦截object-c类的函数,有一种方法是额外写扩展类,例如下面代码:

@interface UIView(fordebug)
- (void)removeFromSuperview;
@end

@implementation UIView(fordebug)

- (void)removeFromSuperview{
    int abcd = 2134;
   // [super removeFromSuperview];
}

@end

以上代码丢到你的工程任意的m文件中,就可以拦截UIView.removeFromSuperView函数的调用了。

但是!有一个缺点,

对于私有函数,hook的时候没法调用原函数,例如上面代码的注释行,编译器会报错,因为UIView.removeFromSuperView并没有公开在UIView中,而是声明在UIView(XXX)的扩展类中。

怎么样才能拦截任意类的函数呢?

彻底支持hook的方法

下面介绍另外一种方法。

1. 首先,添加一个新函数作为拦截时候相应的函数:

static void imp_processViewWillAppear(id self, SEL cmd, BOOL animated){
    
    //先执行原来的方法
    SEL oriSel = sel_getUid("hook_viewWillAppear:");
    void (*hook_viewWillAppear)(id, SEL, BOOL) = (void (*)(id,SEL,BOOL))[UIViewController instanceMethodForSelector:oriSel];//函数指针
    hook_viewWillAppear(self,cmd,animated);
    
    do sth。。。
}

 2. 然后让被hook的函数指向新函数

methodExchange("UIViewController", "viewWillAppear:", "hook_viewWillAppear:", (IMP)imp_processViewWillAppear);

其中,methodExchange代码:


void methodExchange(const char *className, const char *originalMethodName, const char *replacementMethodName, IMP imp) {
    Class cls = objc_getClass(className);//得到指定类的类定义
    SEL oriSEL = sel_getUid(originalMethodName);//把originalMethodName注册到RunTime系统中
    Method oriMethod = class_getInstanceMethod(cls, oriSEL);//获取实例方法
    struct objc_method_description *desc = method_getDescription(oriMethod);//获得指定方法的描述
    assert(desc != NULL);
    if (desc->types) {
        SEL buSel = sel_registerName(replacementMethodName);//把replacementMethodName注册到RunTime系统中
        
        if (class_addMethod(cls, buSel, imp, desc->types)) {//通过运行时,把方法动态添加到类中
            Method buMethod  = class_getInstanceMethod(cls, buSel);//获取实例方法
            method_exchangeImplementations(oriMethod, buMethod);//交换方法
        }
    }
}

如此,就彻底实现了任意类的拦截响应。

有没有更好的?

个人推理,以上两种方法应该是可以结合使用,例如正常的对于私有的函数,再使用以下方法进行调用原函数:

扫描二维码关注公众号,回复: 4171700 查看本文章
@interface UIView(fordebug)
- (void)removeFromSuperview;
@end

@implementation UIView(fordebug)

- (void)removeFromSuperview{
    int abcd = 2134;

   // [super removeFromSuperview];

执行原来的方法
    SEL oriSel = sel_getUid("removeFromSuperview");
    void (*hook_removeFromSuperview)(id, SEL, BOOL) = (void (*)(id,SEL,BOOL))[UIView instanceMethodForSelector:oriSel];//函数指针
    hook_removeFromSuperview(self,cmd);
}

@end

不过这种方法没验证过,理论上评估是可以,读者可以自行试试,

有问题,随时联系,加扣扣群,或者留言。

本文结束。

猜你喜欢

转载自blog.csdn.net/henysugar/article/details/84246976