Xcode13上统计启动时长的变量DYLD_PRINT_STATISTICS生效了。团队中须要保留每次的启动工夫以作测验优化规范。在网上找到上面文章,写了个获取启动工夫工具类。
import "AppLaunchTime.h"
import <sys/sysctl.h>
import <mach/mach.h>
@implementation AppLaunchTime
double __t1; // 创立过程工夫
double __t2; // before main
double __t3; // didfinsh
/// 获取过程创立工夫
(CFAbsoluteTime)processStartTime {
if (__t1 == 0) {
struct kinfo_proc procInfo; int pid = [[NSProcessInfo processInfo] processIdentifier]; int cmd[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; size_t size = sizeof(procInfo); if (sysctl(cmd, sizeof(cmd)/sizeof(*cmd), &procInfo, &size, NULL, 0) == 0) { __t1 = procInfo.kp_proc.p_un.__p_starttime.tv_sec * 1000.0 + procInfo.kp_proc.p_un.__p_starttime.tv_usec / 1000.0; }
}
return __t1;
}
/// 开始记录:在DidFinish中调用
(void)mark {
double __t1 = [LGAppLaunchTime processStartTime];
dispatch_async(dispatch_get_main_queue(), ^{ // 确保didFihish代码执行后调用 if (__t3 == 0) { __t3 = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970; } double pret = __t2 - __t1 / 1000; double didfinish = __t3 - __t2; double total = __t3 - __t1 / 1000; NSLog(@"----------App启动---------耗时:pre-main:%f",pret); NSLog(@"----------App启动---------耗时:didfinish:%f",didfinish); NSLog(@"----------App启动---------耗时:total:%f",total);});
}
/// 构造方法在main调用前调用
/// 获取pre-main()阶段的完结工夫点绝对容易,能够间接取main()主函数的开始执行工夫点.举荐应用__attribute__((constructor)) 构建器函数的被调用工夫点作为pre-main()阶段完结工夫点:__t2能最大水平实现解耦:
void static __attribute__((constructor)) before_main() {
if (__t2 == 0) { __t2 = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970;}
}
@end
复制代码
(BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {
[LGAppLaunchTime mark];
...
复制代码
pre-main()阶段开始工夫:__t1 苹果公司并没有间接向开发者提供外部统计工夫字段以供开发者间接获取App的启动开始时刻点,目前行业内次要有两种规范作为App的启动工夫点:
第一种规范:以名称+(void)load办法被调用时的工夫点 因为+(void)load 办法被调用的工夫点产生Initializer初始化配置阶段, 依据(CompileSources)编译资源规定下动静库的加载程序程序的调用相应类下的+(void)load办法,因为动静库的加载程序是递归加载的,所以咱们只有找到最外部的叶子节点的动静库,而后在这个最外部叶子结点动静库中+(void)load办法重构以记录启动工夫作为开始工夫点。这种形式没有统计到Initializer初始化配置阶段后面局部所耗费的那些工夫,比方在Initializer初始化配置阶段后面减少动静库、Category等造成的耗时并不能被及时发现统计。
第二种规范:获取整个过程创立(从开始到完结)耗费工夫 App从源头配置直至运行整个过程实际上是一个逻辑过程,如果能获取到逻辑过程的起步创立工夫即exec()可执行函数触发阶段的触动工夫点作为整个app逻辑过程的开始工夫点,可能更提前记录到App的启动开始工夫点。
pre-main()阶段完结工夫点:__t2
获取pre-main()阶段的完结工夫点绝对容易,能够间接取main()主函数的开始执行工夫点。 举荐应用__attribute__((constructor)) 构建器函数的被调用工夫点作为pre-main()阶段完结工夫点:__t2
为什么不必最初一个load办法执行工夫作为pre-main()阶段的完结工夫点?因为在超大型工程中咱们没方法确定哪个名称load办法是最初一个被执行load办法。。。
启动动作正式实现对应的工夫点:__t3 启动动作正式实现对应的工夫点个别以didFinishLaunchingWithOptions:已实现启动对应的代理协定函数的完结工夫点,但didFinishLaunchingWithOptions:已实现启动对应的代理协定函数的完结工夫点(仅仅对应着光点初步渲染呈现)其实不包含光点呈现之后启动图动画渲染的工夫耗费,而启动图动画执行实现后的工夫点更加靠近于用户的感官。能够在执行didFinishLaunchingWithOptions: 的runloop循环的前面的循环来获取工夫.
最初
如果你感觉此文对你有一丁点帮忙,点个赞。或者能够退出我的开发交换群:1025263163互相学习,咱们会有业余的技术答疑解惑
如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点star: https://gitee.com/ZhongBangKe...不胜感激 !