返回

循环 Hook 的坑:揭秘 Method Swizzle 的潜在问题

IOS

实战 Method Swizzle 的循环 Hook 问题

在这个数据为王的时代,市面上有用户访问的 App 都会进行日志打点。如果一个个页面去打点,实在费时费力。我们不免想通过 AOP 方式去 Hook 我们想要的方法,就能做到一次打点,统一管理的目的。

比如对于一个页面的进出,只需要对 viewWillAppear 和 viewDidDisappear 这两个方法进行 Hook,就能统一打点进入和离开页面的日志。

而对于 Method Swizzle 这种动态替换方法实现的技术,是 AOP 的一种实现方式。它通过替换方法的实现,达到我们修改方法行为的目的。

但是,Method Swizzle 在使用过程中,也存在着一些潜在的问题,需要我们注意。其中,循环 Hook 就是一个典型的问题。

什么是循环 Hook

循环 Hook 顾名思义,就是在一个方法中,不断地调用自身,从而形成一个无限循环。

在 Method Swizzle 中,如果我们对一个方法进行 Hook,并且在这个 Hook 方法中又调用了这个方法,就会形成循环 Hook。

循环 Hook 的危害

循环 Hook 会导致以下问题:

  • 内存泄漏: 不断地调用自身,会不断地创建新的对象,从而导致内存泄漏。
  • 栈溢出: 不断地调用自身,会不断地压栈,从而导致栈溢出。
  • 死锁: 如果两个方法互相 Hook,并都在 Hook 方法中调用对方,就会形成死锁。

如何避免循环 Hook

为了避免循环 Hook,我们可以采用以下措施:

  • 使用断点调试: 在 Xcode 中,可以在被 Hook 的方法中设置断点,当断点被触发多次时,说明出现了循环 Hook。
  • 使用工具检测: 可以使用一些工具,如 Instruments,来检测循环 Hook。
  • 合理设计 Hook 方法: 在 Hook 方法中,不要调用被 Hook 的方法。如果需要调用被 Hook 的方法,可以使用不同的名称或使用间接调用。

示例代码

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    // 循环 Hook
    [self viewWillAppear:animated];
}

修复方法

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    // 修复循环 Hook
    if (![self isMemberOfClass:[self class]]) {
        [self viewWillAppear:animated];
    }
}

总结

Method Swizzle 是 AOP 的一种实现方式,在使用过程中,需要注意循环 Hook 问题。通过合理的设计 Hook 方法,并使用断点调试或工具检测,可以有效避免循环 Hook 的发生。