这是我参与11月更文挑战的第1天,活动概况查看:2021最终一次更文挑战
发动时刻监控
关于发动时刻监控,只需要搞明白两件工作就完了
- 发动时刻说的是哪一段时刻
- 这段时刻怎么监控
发动时刻到底是哪一段时刻
从用户视点看发动时刻
从用户视点看,发动时刻分为两种衡量办法:
-
从点击图标到主页数据加载完毕
- 比方主页是加载一张网络图片,那么发动时刻便是从点击运用图标到主页图片显示出来的这个时刻
-
从点击图标到Launch Image消失后的榜首帧呈现
- 比方同样是主页加载一张网络图片,那么发动时刻便是从点击运用图标到看到主页的这个时刻(那一刻图片还没加载出来,只能看到一个主页title之类的)
因为不同运用的主页加载数据量不同,所以选用榜首点很难对齐,所以最好按照第二点来衡量
从代码层面看发动时刻
从代码视点看,官方给出了一种核算办法
- 开端时刻为进程创立时刻
- 完毕时刻是榜首个
CA::Transaction::commit()
-
CA::Transaction::commit()
是 Core Animation 提供的一种事务机制,把一组 UI 上的修正打包,一起发给 Render Server 烘托。什么意思呢,便是提交一组UI到GPU进行绘制
-
两种发动办法
运用有两种发动办法:
-
冷发动
- 冷发动便是体系里面没有进程缓存信息时的发动,比方手机重启后榜首次发动、或者运用杀掉后时刻过长体系缓存中进程缓存现已被整理,之后的榜首次发动也是冷发动
-
热发动
- 热发动便是指体系中进程缓存还在时的发动,比方运用刚杀掉又立即点击运用进入
热发动因为运用了进程缓存所以速度会快一些,所以后续所说的发动一般指冷发动
发动时刻怎么监控
这儿有两个部分:
- 发动进程的开端时刻和完毕时刻怎么获取
- 发动进程中各个阶段的时刻怎么获取
全体发动时刻监控
发动进程的开端时刻和完毕时刻怎么获取
开端时刻
点击运用图标后的榜首步便是创立进程,所以开端时刻便是进程创立时刻
获取进程创立时刻能够经过当前进程标识(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;
}
完毕时刻
刚刚也分析了完毕时刻有两种算法:
- Launch Image消失后的榜首帧呈现
- 榜首个
CA::Transaction::commit()
履行
Launch Image消失后的榜首帧呈现
- iOS 12 及以下:root viewController 的 viewDidAppear
- iOS 13+:applicationDidBecomeActive
榜首个CA::Transaction::commit()
履行
这个办法咱们无法直接拿到榜首次履行的时刻,但是能够经过其他事件拿到挨近这个点的时刻
CFRunLoopPerformBlock
在CA::Transaction::commit()
履行之前调用,kCFRunLoopBeforeTimers
在CA::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);
});
各个阶段的时刻怎么获取
总的发动时刻有了,接下来便是核算各个阶段的发动时刻,首先要知道发动进程中有哪些阶段,运用发动进程通常咱们分三个阶段
-
main
函数之前,pre-main阶段 -
main
函数之后 - 首屏烘托完结之后
main
函数之前
main
函数之前的阶段指的是从点击图标到履行main函数之前,咱们称为pre-main阶段,这个阶段主要做一下几个工作:
- 加载可履行文件(app的.o文件的集合)
- 加载动态连接库
- 进行rebase指针调整和bind符号绑定
- OC运行时的初始处理(OC相关类的注册,category注册,selector唯一性检查等)
-
初始化(履行
+load()
办法,attribute(constructor)
修饰的函数的调用、创立C++静态全局变量)
这儿先大致知道做了哪些工作,后续会对每个工作做详细介绍
pre-main阶段时刻监控
Apple提供了一种丈量办法,在 Xcode 中 Edit scheme -> Run -> Auguments
将环境变量 DYLD_PRINT_STATISTICS
设为1 。之后控制台会输出类似内容,咱们能够明晰的看到
整个pre-main 是1.3秒,下面还有各个部分的详细时刻
main
函数之后
这个阶段是指从main
函数履行开端到appDelegate
的didFinishLaunchingWithOptions
办法里面首屏烘托相关办法履行完结,
即,从main
函数履行到设置self.window.rootViewController
履行完结的阶段。
main函数之后主要做一下工作:
- 首屏初始化所需配置文件的读写操作
- 首屏列表大数据的读取
- 首屏烘托的很多核算
main
函数之后的时刻监控
根据这个阶段的履行进程能够得出:
- 开端时刻能够在main函数刚开端时丈量
- 完毕时刻能够在
self.window.rootViewController
办法之后丈量
首屏烘托完结之后
这个阶段是指在首屏烘托完结后到 didFinishLaunchingWithOptions
完毕这段内的办法履行
- 开端时刻便是在
self.window.rootViewController
办法之后丈量 - 完毕时刻便是
didFinishLaunchingWithOptions
办法完毕
以上是经过代码来丈量发动时刻,其实还能够经过东西来丈量时刻
1.运用time profiler 监控
2.运用 app launch东西丈量
这儿暂不介绍。 以上,欢迎我们谈论沟通。