这是我参与11月更文挑战的第1天,活动概况查看:2021最终一次更文挑战

发动时刻监控

关于发动时刻监控,只需要搞明白两件工作就完了

  1. 发动时刻说的是哪一段时刻
  2. 这段时刻怎么监控

发动时刻到底是哪一段时刻

从用户视点看发动时刻

从用户视点看,发动时刻分为两种衡量办法:

  1. 从点击图标到主页数据加载完毕
    • 比方主页是加载一张网络图片,那么发动时刻便是从点击运用图标到主页图片显示出来的这个时刻
  2. 从点击图标到Launch Image消失后的榜首帧呈现
    • 比方同样是主页加载一张网络图片,那么发动时刻便是从点击运用图标到看到主页的这个时刻(那一刻图片还没加载出来,只能看到一个主页title之类的)

因为不同运用的主页加载数据量不同,所以选用榜首点很难对齐,所以最好按照第二点来衡量

从代码层面看发动时刻

从代码视点看,官方给出了一种核算办法

  • 开端时刻为进程创立时刻
  • 完毕时刻是榜首个CA::Transaction::commit()
    • CA::Transaction::commit()是 Core Animation 提供的一种事务机制,把一组 UI 上的修正打包,一起发给 Render Server 烘托。什么意思呢,便是提交一组UI到GPU进行绘制

两种发动办法

运用有两种发动办法:

  1. 冷发动

    • 冷发动便是体系里面没有进程缓存信息时的发动,比方手机重启后榜首次发动、或者运用杀掉后时刻过长体系缓存中进程缓存现已被整理,之后的榜首次发动也是冷发动
  2. 热发动

    • 热发动便是指体系中进程缓存还在时的发动,比方运用刚杀掉又立即点击运用进入

热发动因为运用了进程缓存所以速度会快一些,所以后续所说的发动一般指冷发动

发动时刻怎么监控

这儿有两个部分:

  1. 发动进程的开端时刻和完毕时刻怎么获取
  2. 发动进程中各个阶段的时刻怎么获取

全体发动时刻监控

发动进程的开端时刻和完毕时刻怎么获取

开端时刻

点击运用图标后的榜首步便是创立进程,所以开端时刻便是进程创立时刻 获取进程创立时刻能够经过当前进程标识(NSProcessInfo\processIdentifier),读取进程信息内的进程创立时刻(__p_starttime)为发动时刻

#import <sys/sysctl.h>
#import <mach/mach.h>
+ (NSTimeInterval)processStartTime
{   // 单位是毫秒
    struct kinfo_proc kProcInfo;
    if ([self processInfoForPID:[[NSProcessInfo processInfo] processIdentifier] procInfo:&kProcInfo]) {
        return kProcInfo.kp_proc.p_un.__p_starttime.tv_sec + kProcInfo.kp_proc.p_un.__p_starttime.tv_usec / 1000000.0;
    } else {
        NSAssert(NO, @"无法取得进程的信息");
        return 0;
    }
}
+ (BOOL)processInfoForPID:(int)pid procInfo:(struct kinfo_proc*)procInfo
{
    int cmd[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    size_t size = sizeof(*procInfo);
    return sysctl(cmd, sizeof(cmd)/sizeof(*cmd), procInfo, &size, NULL, 0) == 0;
}

完毕时刻

刚刚也分析了完毕时刻有两种算法:

  1. Launch Image消失后的榜首帧呈现
  2. 榜首个CA::Transaction::commit()履行
Launch Image消失后的榜首帧呈现
  • iOS 12 及以下:root viewController 的 viewDidAppear
  • iOS 13+:applicationDidBecomeActive
榜首个CA::Transaction::commit()履行

这个办法咱们无法直接拿到榜首次履行的时刻,但是能够经过其他事件拿到挨近这个点的时刻

iOS基础学习-1-启动时间监控
CFRunLoopPerformBlockCA::Transaction::commit()履行之前调用,kCFRunLoopBeforeTimersCA::Transaction::commit()履行之后调用

  • iOS13(含)以上的体系选用 runloop 中注册一个 kCFRunLoopBeforeTimers 的回调获取到的 App 首屏烘托完结的机遇更精确。
//注册kCFRunLoopBeforeTimers回调
CFRunLoopRef mainRunloop = [[NSRunLoop mainRunLoop] getCFRunLoop];
CFRunLoopActivity activities = kCFRunLoopAllActivities;
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, activities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    if (activity == kCFRunLoopBeforeTimers) {
        NSTimeInterval stamp = [[NSDate date] timeIntervalSince1970];
        NSLog(@"runloop beforetimers launch end:%f",stamp);
        CFRunLoopRemoveObserver(mainRunloop, observer, kCFRunLoopCommonModes);
    }
});
CFRunLoopAddObserver(mainRunloop, observer, kCFRunLoopCommonModes);
  • iOS13 以下的体系选用 CFRunLoopPerformBlock 办法注入 block 获取到的 App 首屏烘托完结的机遇更精确。
//注册block
CFRunLoopRef mainRunloop = [[NSRunLoop mainRunLoop] getCFRunLoop];
CFRunLoopPerformBlock(mainRunloop,NSDefaultRunLoopMode,^(){
    NSTimeInterval stamp = [[NSDate date] timeIntervalSince1970];
    NSLog(@"runloop block launch end:%f",stamp);
});

各个阶段的时刻怎么获取

总的发动时刻有了,接下来便是核算各个阶段的发动时刻,首先要知道发动进程中有哪些阶段,运用发动进程通常咱们分三个阶段

  1. main 函数之前,pre-main阶段
  2. main 函数之后
  3. 首屏烘托完结之后

main 函数之前

main 函数之前的阶段指的是从点击图标到履行main函数之前,咱们称为pre-main阶段,这个阶段主要做一下几个工作:

  1. 加载可履行文件(app的.o文件的集合)
  2. 加载动态连接库
  3. 进行rebase指针调整和bind符号绑定
  4. OC运行时的初始处理(OC相关类的注册,category注册,selector唯一性检查等)
  5. 初始化(履行+load()办法,attribute(constructor)修饰的函数的调用、创立C++静态全局变量)

这儿先大致知道做了哪些工作,后续会对每个工作做详细介绍

pre-main阶段时刻监控

Apple提供了一种丈量办法,在 Xcode 中 Edit scheme -> Run -> Auguments 将环境变量 DYLD_PRINT_STATISTICS 设为1 。之后控制台会输出类似内容,咱们能够明晰的看到

iOS基础学习-1-启动时间监控
整个pre-main 是1.3秒,下面还有各个部分的详细时刻

main 函数之后

这个阶段是指从main函数履行开端到appDelegatedidFinishLaunchingWithOptions办法里面首屏烘托相关办法履行完结, 即,从main函数履行到设置self.window.rootViewController履行完结的阶段。 main函数之后主要做一下工作:

  1. 首屏初始化所需配置文件的读写操作
  2. 首屏列表大数据的读取
  3. 首屏烘托的很多核算
main 函数之后的时刻监控

根据这个阶段的履行进程能够得出:

  • 开端时刻能够在main函数刚开端时丈量
  • 完毕时刻能够在self.window.rootViewController办法之后丈量

首屏烘托完结之后

这个阶段是指在首屏烘托完结后到 didFinishLaunchingWithOptions完毕这段内的办法履行

  • 开端时刻便是在self.window.rootViewController办法之后丈量
  • 完毕时刻便是didFinishLaunchingWithOptions办法完毕

以上是经过代码来丈量发动时刻,其实还能够经过东西来丈量时刻

1.运用time profiler 监控

2.运用 app launch东西丈量

这儿暂不介绍。 以上,欢迎我们谈论沟通