iOS App启动优化(四):编译期插桩 && 获取方法符号
iOS App启动优化(五):收集符号 && 生成 Order File
前言
没看过前四篇的胖友们不用担心,在这里直接贴上 demo
的当前主要代码
主要做了以下几件事情:
- 声明了一个
block
、一个c函数func()
、一个oc函数-(void)test
、一个Swift
编写的含有类方法swiftTest
的类 - 在
touchesBegan:withEvent:
方法中调用了-(void)test
,-(void)test
里面把上述方法嵌套调用,最终会打印字符串test
- 添加编译期插桩的相关方法
__sanitizer_cov_trace_pc_guard_init
和__sanitizer_cov_trace_pc_guard
- 通过编译期插桩的相关方法获取到方法符号等信息,详见代码
收集符号
Focus 1 多线程
启动的相关方法可能在不同的线程执行,如果我们用一个数组直接收集这些符号,会出现线程问题。
听到多线程问题立马想到锁,但是这里因为锁耗费性能比较多所以不推荐使用。建议使用原子队列解决这个问题。
原子队列是栈结构,通过 队列结构 + 原子性 保证顺序。
导入头文件
#import <libkern/OSAtomic.h>
启动的时候方法执行,__sanitizer_cov_trace_pc_guard
获取到的 PC
会作为结构体 Node
的成员变量以链表的形式存储下来。
当我们点击屏幕时,touchesBegan:withEvent:
里面会倒序取出节点,通过节点内的成员变量 pc
生成 Dl_info
,再从 info
中读取方法符号。通过去重判断后插入到方法符号数组arr
的头部,使最终记录的方法执行顺序是正序。
执行一下代码看看
这时候尴尬了,一直在-[ViewController touchesBegan:withEvent:]
里死循环。
Focus 2 处理While循环
断点看汇编
__sanitizer_cov_trace_pc_guard
竟然在反复横跳?
文档里说 __sanitizer_cov_trace_pc_guard
会在每个边缘级别插入,那么每执行一次 while
循环应该算是一次边界!
解决方案: 修改为只 hook
函数, Target -> Build Setting -> Custom Complier Flags -> Other C Flags
修改为 -fsanitize-coverage=func,trace-pc-guard
再次运行代码
这个时候,我们已经获取到了正确的方法符号。
Focus 3 处理load函数
在当前类添加 load
方法后执行看输出,发现 load
并没有被打印。
load
方法调用时插入的 __sanitizer_cov_trace_pc_guard
参数 guard
为0,默认的函数实现会直接return
,导致无法捕获到 load
。
屏蔽掉 __sanitizer_cov_trace_pc_guard
中的 if (!*guard) return;
即可
成功收集到了启动相关的所有方法符号。
Focus 3 缺失的符号处理
我们看到输出的函数符号里面缺少了之前声明的函数(test
、func
、swiftTest
)和 block
,这是因为我没有去调用他们。
只有被调用的函数才会被__sanitizer_cov_trace_pc_guard
捕获。
在 viewDidLoad
里面调用一下
Focus 4 处理c函数
和block
的符号
生成 Order File
前还需要对 c函数
和 block
做特殊处理。
在 objc-750
的 order file
看到这两种符号都是以下划线 "_" 开头的。而我们获取到的是没有下划线的,所以要拼接上去。
生成 Order File
生成 Order File
就是把上面加工好的方法符号集合拼接成字符串并写入文件。
记得移除掉点击触发的touchesBegan:withEvent:
的符号。
看一下我们的 Order File
完美~ 只要把 Order File
放入工程目录并设置好路径,就完完整整的实现了启动优化~
完结撒花~ 🎉🎉🎉🎉🎉🎉
不爱原理,直接开干的胖友们
看优化效果对比的胖友们