ios - 有什么方法可以列出 iOS 应用程序中的所有 swizzled 方法吗?

标签 ios objective-c objective-c-runtime method-swizzling

我本质上是在寻找一种方法来检测第三方库何时/哪些第三方库出现问题。我最近遇到了一个广告库使用 AFNetworking 的古怪分支的情况。 AFNetworking swizzles NSURLSessionTask,这两个swizzles在某些情况下表现不佳。我真的很想能够检测和健全检查这类事情,理想情况下甚至在应用程序中保留每个混合方法的版本转储,这样我们就可以了解谁在修补什么以及风险是什么。 Google 和 stack overflow 搜索只找到了一堆关于如何调配的教程。有人遇到这个问题或有解决方案吗?看起来我可以使用 objc/runtime.h 编写一些代码,但我无法想象我是第一个需要它的人。

最佳答案

经过几个小时的修补,这是我能够得到的最接近的结果。它涉及使用 mach_override 的 fork ,一些关于加载顺序的 DYLD 怪癖,以及对疯狂黑客的胃口。

它只能在模拟器上运行,但这应该足以满足您似乎拥有的用例(我当然希望您没有仅设备依赖项)。

代码的主体看起来像这样:

#include <objc/runtime.h>
#include <mach_override/mach_override.h>

// It is extremely important that we have DYLD run this constructor as soon as the binary loads. If we were to hook
// this at any other point (for example, another category on NSObject, in the main application), what could potentially
// happen is other `+load` implementations get invoked before we have a chance to hook `method_exchangeImplementation`,
// and we don't get to see those swizzles.
// It is also extremely important that this exists inside its own dylib, which will be loaded by DYLD before _main() is
// initialized. You must also make sure that this gets loaded BEFORE any other userland dylibs, which can be enforced by
// looking at the order of the 'link binary with libraries' phase.
__attribute__((constructor))
static void _hook_objc_runtime() {
  kern_return_t err;
  MACH_OVERRIDE(void, method_exchangeImplementations, (Method m1, Method m2), &err) {
    printf("Exchanging implementations for method %s and %s.\n", sel_getName(method_getName(m1)), sel_getName(method_getName(m2)));

    method_exchangeImplementations_reenter(m1, m2);
  }
  END_MACH_OVERRIDE(method_exchangeImplementations);

  MACH_OVERRIDE(void, method_setImplementation, (Method method, IMP imp), &err) {
    printf("Setting new implementation for method %s.\n", sel_getName(method_getName(method)));

    method_setImplementation_reenter(method, imp);
  }
  END_MACH_OVERRIDE(method_setImplementation);
}

这非常简单,并产生如下输出:

Exchanging implementations for method description and custom_description.

没有很好的方法(不使用断点和查看堆栈跟踪)来确定正在调配哪个类,但对于大多数情况,只需看一眼选择器就可以提示您要去哪里从那里开始。

它似乎适用于我在 +load 期间创建的几个类别,从我对 mach_override 和 DYLD 内部的阅读来看,只要你已经正确设置了你的库加载顺序,你可以期望它在任何其他用户代码之前被初始化,如果你把它放在它自己的动态库中。

现在,我不能保证它的安全性,但作为调试工具保留它似乎很有用,所以我已经将我的示例发布到 github:

https://github.com/richardjrossiii/mach_override_example

它已获得 MIT 许可,因此您可以随意使用它。

关于ios - 有什么方法可以列出 iOS 应用程序中的所有 swizzled 方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38488312/

相关文章:

iphone - CSLogItem 时间戳格式(NSTimeInterval 从什么时候开始?)

rust - 编码 SIMD 类型和将它们用于 Objective-C 运行时的函数的正确方法是什么?

objective-c - 从 Objective-C block 创建 IMP

ios - 是否可以在 TestFlight 中同时激活内部和外部构建?

ios - Swift:将 View Controller 的类型更改为 UIViewController 以外的类型

ios - 当尝试快速从数组中删除时,不应删除表格 View 单元格中的最后一个单元格

ios - NSURLSession 准确设置请求超时

ios - 从深层 JSON 结构构建 UITableView

iphone - 通过界面生成器将 subview 添加到 UIImageView

objective-c - 为什么 Objective-C 不支持私有(private)方法?