iOS App启动优化(六):实用党直接看这里

5,886 阅读2分钟

iOS App启动优化(一):检测启动时间

iOS App启动优化(二):物理内存和虚拟内存

iOS App启动优化(三):二进制重排

iOS App启动优化(四):编译期插桩 && 获取方法符号

iOS App启动优化(五):收集符号 && 生成 Order File

iOS App启动优化(六):实用党直接看这里

效果对比

优化前

优化后

步骤一:添加 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 以后编译代码