iOS App启动优化(四):编译期插桩 && 获取方法符号
iOS App启动优化(五):收集符号 && 生成 Order File
效果对比
优化前
优化后
步骤一:添加 Build Setting 设置
Target -> Build Setting -> Custom Complier Flags ->
Other C Flags
添加
-fsanitize-coverage=func,trace-pc-guard
Other Swift Flags
添加
-sanitize-coverage=func
-sanitize=undefined
步骤二:添加代码
添加到启动最早的那个 ViewController
即可。
#import "dlfcn.h"
#import <libkern/OSAtomic.h>
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
static uint64_t N; // Counter for the guards.
if (start == stop || *start) return; // Initialize only once.
printf("INIT: %p %p\n", start, stop);
for (uint32_t *x = start; x < stop; x++)
*x = ++N; // Guards should start from 1.
}
//初始化原子队列
static OSQueueHead list = OS_ATOMIC_QUEUE_INIT;
//定义节点结构体
typedef struct {
void *pc; //存下获取到的PC
void *next; //指向下一个节点
} Node;
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
void *PC = __builtin_return_address(0);
Node *node = malloc(sizeof(Node));
*node = (Node){PC, NULL};
// offsetof() 计算出列尾,OSAtomicEnqueue() 把 node 加入 list 尾巴
OSAtomicEnqueue(&list, node, offsetof(Node, next));
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSMutableArray *arr = [NSMutableArray array];
while(1){
//有进就有出,这个方法和 OSAtomicEnqueue() 类比使用
Node *node = OSAtomicDequeue(&list, offsetof(Node, next));
//退出机制
if (node == NULL) {
break;
}
//获取函数信息
Dl_info info;
dladdr(node->pc, &info);
NSString *sname = [NSString stringWithCString:info.dli_sname encoding:NSUTF8StringEncoding];
printf("%s \n", info.dli_sname);
//处理c函数及block前缀
BOOL isObjc = [sname hasPrefix:@"+["] || [sname hasPrefix:@"-["];
//c函数及block需要在开头添加下划线
sname = isObjc ? sname: [@"_" stringByAppendingString:sname];
//去重
if (![arr containsObject:sname]) {
//因为入栈的时候是从上至下,取出的时候方向是从下至上,那么就需要倒序,直接插在数组头部即可
[arr insertObject:sname atIndex:0];
}
}
//去掉 touchesBegan 方法 启动的时候不会用到这个
[arr removeObject:[NSString stringWithFormat:@"%s",__FUNCTION__]];
//数组合成字符串
NSString * funcStr = [arr componentsJoinedByString:@"\n"];
//写入文件
NSString * filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"link.order"];
NSData * fileContents = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
NSLog(@"%@", filePath);
[[NSFileManager defaultManager] createFileAtPath:filePath contents:fileContents attributes:nil];
}
步骤三:取出 order file
- 在步骤二的代码
NSLog(@"%@", filePath);
断点 - 如果页面无法触发点击,
viewDidLoad
里面调用touchesBegan:withEvent:
也可以 - 运行代码后记录
link.order
的路径 Finder
前往路径取出order file
步骤四:设置 order file
- 把
link.order
的路径放到工程根目录 Target -> Build Setting -> Linking -> Order File
设置路径
步骤五:编译代码
- 把步骤一
order file
的设置还原 - 把步骤二添加代码删除
clean
以后编译代码