近期的一个项目里,我们自己开发的埋点模块和友盟 SDK 在兼容上发生了点问题,定位问题的过程中,想看看友盟 SDK 里有哪些类,但是友盟 SDK 只提供了 3 个 public header:

办法其实也很简单,用 runtime 很容易 get 到当前项目里都有哪些类文件被编译进来了,即使打包成 .a .framework 的 SDK 也不例外
更新:runtime 的方式在文末,先说简单的方式:
一、获取 SDK 里的私有类名
使用 lipo + ar 命令:
1 | cd UMMobClick.framework |
以下是输出结果
1 | DplusMobClick.o TException.o UMAggregatedValue.o |
二、使用 LLDB 命令打印一个类的全部属性、方法
在上一步获取到类名的情况下,使用简单的一行 LLDB 命令,就可以获取到一个类的属性、方法,不用写一行代码,且打印内容也便于阅读:
在运行着的项目任意位置打断点,在控制台 po 需要打印的类名 + _shortMethodDescription:,eg:
1 | po [MobClickSession _shortMethodDescription] |
输出:
1 | <MobClickSession: 0x108788af8>: |
当然使用 runtime 的方式,也可以实现以上需求,以下是探索过程:
三、使用 runtime 的方式获取 SDK 的私有类名
执行以下代码:
1 |
|
输出:
1 | 2017-09-09 14:42:20.248 当前项目中全部 class WKObject |
可以看到,把项目中导入的系统的 UIKit.framework Foundation.framework 都打出来了,如果我们再人肉排除 UI、_UI、NS 打头的类:
1 | Class clazz = classes[i]; |
结果如下:
1 | 2017-09-09 15:06:35.995 当前项目中全部 class: WKObject |
系统框架类名前缀也是五花八门,if else 的排除是不可能了,得换个思路。
那么,到底怎么区分一个类是系统的,还是用户自定义的呢?查了一下,还真没有办法区分,因为这个问题根本就不成立:
所谓系统的类,说白了就是苹果提供的 framework 里的类,也都是苹果自己自定义的。
中文搜不到,找了一圈在 stackoverflow 找到了这个:How to judge a class whether System’s or Custom’s?
简单点说就是,虽然区分不了什么系统不系统的类,但是 Framework 是分动态库和静态库的。而苹果开发的 Framework 都是以动态库的形式参与编译打包,只链接到 app 里,不会打包到 App 的 Bundle 中。但所有除了苹果以外的开发者,都只能以静态库 Framework 打包 SDK(上架),代码都会被打包到 App 的 Bundle 中,那么问题就简单了:
1 | - (void)showCustomClassNameOnly { |
输出结果如下:
1 | 2017-09-09 14:43:27.291 自定义 class: UMTMemoryBuffer |
这样,排除 Demo 项目里的 AppDelegate、ViewController 这两个类,我们就能看到友盟 SDK 里的全部类名了。
Note
1、在 FLEX 源码中,发现了更简单的实现方式:
1 | - (void)flexShowClassNames { |
2、objc_getClassList 和 objc_copyClassList 用法区别
objc_getClassList需要两次才能获取,较麻烦
1 | // int objc_getClassList(Class *buffer, int bufferCount) 获取已经注册的类 |
objc_copyClassList代码相对简单:
1 | // Class *objc_copyClassList(unsigned int *outCount) |
四、使用 runtime 打印一个类的全部属性、方法
在获取到 SDK 私有类的文件名后,还需要进一步获取类的属性、方法、遵守的协议。
简单的调用几个 runtime 的 API 就可以做到 ,代码参见 Demo
,效果如下:
1 | ivar[0] ---- B : _observerRegistered |
Demo
注:runtime 的方案,无法打印出 SDK 里的 Category 类
