iOS启动优化

1,584 阅读2分钟

1. 什么是启动?

启动类型

image.png 启动三种类型:冷启动 热启动 重启

启动阶段

image.png

1.1 System Inteface Dyld3

image.png

image.png

1.2 System Inteface libSystem Init

image.png

2. Runtime Init Static Runtime Initialization

image.png

image.png

3. UIKit Init

image.png

image.png

4. Appliciation Initializtion

image.png

image.png

5. Initial Frame Render

image.png

image.png

6. Extend

image.png

image.png

2. 如何正确测量?

image.png

1. XCTest

image.png

2. Use Instruments

image.png

1. Minimize

image.png

2. Prioritize work

image.png

3. Optimize

image.png

image.png

image.png

image.png

image.png

二进制重排

将启动时需要调用的函数放到一起 ( 比如 前10页中 ) 以尽可能减少 page fault , 达到优化目的 . 而这个做法就叫做 : 二进制重排 .

具体操作:

  • 如何检测 page fault : 首先我们要想看到优化效果 , 就应该知道如何查看 page fault , 以此来帮助我们查看优化前以及优化后的效果 .

打开link map File 添加order文件 工程配置

借助clang 官方文档 clang Tracing PC 跟踪PC指针

通过clang排列二进制

代码的二进制,代码的实现的二进制。

  1. 配置clang 插桩
  2. clang插桩的原理(只要添加了clang插桩的标记,那么便一起就会在所有方法函数block代码实现边缘添加一行代码。 执行回调函数。)
  3. 获取符号(函数调用栈,其实就是通过return/)
  4. 通过原子队列保存符号
  5. 解决天坑,仅拦截方法
  6. 符号去重,生成order文件
#import <libkern/OSAtomic.h>
#include <sanitizer/coverage_interface.h>
#include <dlfcn.h>

// 定义原子队列
static OSQueueHead sybmolList = OS_ATOMIC_QUEUE_INIT;
// 定义符号结构体
typedef struct {
    void *pc;
    void *next;
} SYNNode;
- (void)testSleep{
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.
}

// 可以拦截一切的方法调用
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  if (!*guard) return;  // Duplicate the guard check.
  void *PC = __builtin_return_address(0);
    // 多线程访问数据的方法 线程安全
    // 创建结构体
    SYNNode * node = malloc(sizeof(SYNNode));
    *node = (SYNNode){PC,NULL};
    
    // 结构体入栈
    OSAtomicEnqueue(&sybmolList, node, offsetof(SYNNode, next));
}



- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSMutableArray<NSString *> *symbolNames = [NSMutableArray new];
    while(YES){
        SYNNode *node = OSAtomicDequeue(&sybmolList,  offsetof(SYNNode, next));
        if (node == NULL) {
            break;
        }
        Dl_info info;
        dladdr(node->pc, &info);
        NSString *name = @(info.dli_sname);
        
        // 给函数名称添加_
        BOOL isObjc = [name hasPrefix:@"+["] || [name hasPrefix:@"-["];
        NSString *symbolName = isObjc ? name :[@"_" stringByAppendingString:name];
        [symbolNames addObject:symbolName];
    }
    NSEnumerator *em = [symbolNames reverseObjectEnumerator];
    NSMutableArray *funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];
    
    NSString *name;
    // 有序去重
    while (name = [em nextObject]) {
        if(![funcs containsObject:name]) {
            [funcs addObject:name];
        }
    }
    
    //去掉自己
    [funcs removeObject:[NSString stringWithFormat:@"%s",__func__]];
    
    // 写入文件
    // 转成字符串
    NSString * funcStr = [funcs componentsJoinedByString:@"\n"];
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"caoliang.order"];
    
    // 转成二进制
    NSData *file = [funcStr dataUsingEncoding:NSUTF8StringEncoding];
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:file attributes:nil];
    
    NSLog(@"%@",funcStr);
}
  1. swift 符号覆盖 other Swift Flag 添加标记 -sanitize-coverage=func -sanitize=undefined

参考: 启动优化