iOS 测试 iOS 启动时间测试

xinxi · December 11, 2018 · Last by 李鹏 replied at September 06, 2019 · 4750 hits
本帖已被设为精华帖!

前言

在刚接触 iOS 专项测试的时候就开始调研测试 iOS 启动时间,经过一段时间的学习和实践,介绍两种 iOS 启动时间测试的思路.

iOS 应用生命周期

状态如下:

  • Not running: 未运行 程序没启动
  • Inactive: 未激活 程序在前台运行,不过没有接收到事件。在没有事件处理情况下程序通常停留在这个状态
  • Active: 激活 程序在前台运行而且接收到了事件。这也是前台的一个正常的模式
  • Backgroud: 后台 程序在后台而且能执行代码,大多数程序进入这个状态后会在在这个状态上停留一会。时间到之后会进入挂起状态 (Suspended)。有的程序经过特殊的请求后可以长期处于 Backgroud 状态
  • Suspended: 挂起 程序在后台不能执行代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就把挂起的程序清除掉,为前台程序提供更多的内存。

下图是程序状态变化图:

image

iOS app 启动过程

main 函数之前

main 函数之前加载动态库、配置环境等,这个阶段基本上没有开发同学的代码,都是系统的事.

image

main 函数之后

wx20181211-160723.png

  • 1.执行 UIApplicationMain 函数
  • 2.创建 Application 对象,创建 ApplicationDelegate,并设为 Application 的代理
  • 3.创建 RunLoop 主循环,代理开始监听事件
  • 4.启动完毕后会执行 didFinishLaunching,创建 keyWindow,以及 weindow 的跟视图控制器
  • 5.显示窗口

ApplicationDelegate

在 main 函数执行之后会走到 ApplicationDelegate 类中,这是开发同学第一个能控制的类.下面介绍几个 ApplicationDelegate 中的回调方法.

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
      告诉代理进程启动但还没进入状态保存

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
     告诉代理启动基本完成程序准备开始运行

- (void)applicationWillResignActive:(UIApplication *)application
    当应用程序将要入非活动状态执行,在此期间,应用程序不接收消息或事件,比如来电话了

- (void)applicationDidBecomeActive:(UIApplication *)application 
     当应用程序入活动状态执行,这个刚好跟上面那个方法相反

- (void)applicationDidEnterBackground:(UIApplication *)application
    当程序被推送到后台的时候调用。所以要设置后台继续运行,则在这个函数里面设置即可

- (void)applicationWillEnterForeground:(UIApplication *)application
    当程序从后台将要重新回到前台时候调用,这个刚好跟上面的那个方法相。

- (void)applicationWillTerminate:(UIApplication *)application
    当程序将要退出是被调用,通常是用来保存数据和一些退出前的清理工作。这个需要要设置UIApplicationExitsOnSuspend的键值。

- (void)applicationDidFinishLaunching:(UIApplication*)application
    当程序载入后执行

测试

main 函数之前测试

xcode 提供了一个很赞的方法,只需要在 Edit scheme -> Run -> Arguments 中将环境变量
DYLD_PRINT_STATISTICS 设 1,就可以看到 main 之前各个阶段的时间消耗.

image

output:
2018-10-08 11:25:41.101432+0800 xxxxxx[2567:1049814] [DYMTLInitPlatform] platform initialization successful
Total pre-main time: 363.78 milliseconds (100.0%)
         dylib loading time:  56.61 milliseconds (15.5%)
        rebase/binding time:  43.80 milliseconds (12.0%)
            ObjC setup time:  50.18 milliseconds (13.7%)
           initializer time: 213.00 milliseconds (58.5%)
           slowest intializers :
             libSystem.B.dylib :   8.25 milliseconds (2.2%)
   libBacktraceRecording.dylib :   9.38 milliseconds (2.5%)
    libMainThreadChecker.dylib :  13.30 milliseconds (3.6%)
          libglInterpose.dylib :  78.40 milliseconds (21.5%)
         libMTLInterpose.dylib :  21.70 milliseconds (5.9%)
                    LuoJiFMIOS : 107.63 milliseconds (29.5%)

main 函数之后测试

这是用打 log 的方式记录时间.

1.首先在 main.m 添加如下代码

CFAbsoluteTime StartTime;
int main(int argc, char * argv[]) {
    StartTime = CFAbsoluteTimeGetCurrent();

2.然后在 AppDelegate.m 的开头声明
extern CFAbsoluteTime StartTime;
最后在 AppDelegate.m 的
didFinishLaunchingWithOptions 中第一行添加

OptionsStartTime = CFAbsoluteTimeGetCurrent();
NSLog(@"AppLanuch--mainBefore--%f",(OptionsStartTime - StartTime));

这里统计的是 main 函数之前后到 didFinishLaunchingWithOptions 开始.

didFinishLaunchingWithOptions 测试

最后在 AppDelegate.m 的
didFinishLaunchingWithOptions 中最后一行添加

OptionsEndTime = CFAbsoluteTimeGetCurrent();
  NSLog(@"AppLanuch--didOptionsTotal--%f",(OptionsEndTime - OptionsStartTime));

这里是统计这个 didFinishLaunchingWithOptions 函数耗时.

image

统计启动耗时

总耗时=main 函数之前 +main 函数之后 +didFinishLaunchingWithOptions

time profile 工具

time profile 是 xcode 中 Instruments 中的一个测试工具,可以用来测试函数耗时.

time profile

image

启动 app

image

main 方法

image

函数列表

在左侧可以看到每个函数的耗时
image

点击函数

image

跳转到具体函数

image

自动化方案测试

使用 appium 启动 app,同时 idevicesyslog 查看 log 日志并找出启动结束的点.这种方案目前我还没有找出确定的结束点,后续再研究下.

踩坑

在 didFinishLaunchingWithOptions 中注册函数太多

启动以后就在 didFinishLaunchingWithOptions 注册各种函数,可以适当把非必须函数置后注册.

在 didFinishLaunchingWithOptions 中使用第三方 sdk

会注册第三方 sdk,比如 umeng 和 bugly 等注册会导致耗时的可能,如果更换第三方 sdk,需要对其进行性能测试.

结语

想做好 iOS 专项测试,还是要深入了解 iOS 的一些框架和原理,要不很难深入测试和二次开发.

参考文章:

iOS 应用程序生命周期 (前后台切换,应用的各种状态) 详解
https://blog.csdn.net/totogo2010/article/details/8048652

iOS 启动时间优化

https://mp.weixin.qq.com/s/lRBdtwh7BmO6i3yDBRGTwA

iOS-main 函数之前

https://www.jianshu.com/p/7239f13753aa

如何精确度量 iOS App 的启动时间

https://www.jianshu.com/p/c14987eee107

[iOS] 一次立竿见影的启动时间优化

https://juejin.im/post/5a31190751882559e225a775

iOS App 冷启动治理:来自美团外卖的实践

https://mp.weixin.qq.com/s/jN3jaNrvXczZoYIRCWZs7w

time profile 工具使用

https://www.raywenderlich.com/397-instruments-tutorial-with-swift-getting-started

共收到 12 条回复 时间 点赞

大佬能请教个问题么?
关于 appium ios 自动化的~ noReset 参数 设置为 False 的时候,应该是会去重置应用的,
我用模拟器的时候没问题 可以重置,但是真机测试的时候 没有反应 不会去重置~ 能不能帮忙分析下可能是哪里有问题?

wtnhz 回复

Shut down sim after test. Do not destroy sim. Do not uninstall app from real device. 官网的解释...

xinxi 回复

我好像知道问题所在了 谢谢
大佬,那我再问个问题,
Android 的 noReset 参数为 fasle 的时候 是去清理应用 应该就是去执行了一个 pm clear 的命令
ios 的 noReset 参数为 fasle,我之前用 ios 模拟器的时候 好像是去重新安装了一次这个应用
真机可能是我刚刚没有设置 app 这个参数 就没有找到安装包 就没有去重新安装了~
除了 它这个重新安装的方法之外 还有没有别的办法去到达重置应用的效果

wtnhz 回复

google 了一番,iOS 没有清除数据缓存的命令

陈恒捷 将本帖设为了精华贴 18 Dec 23:13

加精理由:从原理到具体方法,讲得挺完整,而且也有相关的参考资料。

PS:印象中 wwdc 中有相关的 topic 专门讲启动时间的,建议也补充到参考资料里,更为完整。

看懂了赞

安涛 [Topic was deleted] 中提及了此贴 21 Dec 16:29

有头无尾,期待后续更新

可以命令方式调用 instrument,会生成一个.trace 文件,然后解析 trace 文件

梦桥 回复

嗨,你用的 Mac 版本、Xcode 版本、测试设备的系统版本分别是多少呀?有没有遇到过命令行收集的 trace 是空的情况。我用命令行会随机的出现 trace 为空的情况。

梦桥 回复

trace 文件怎么解析??

wumeijun 回复

可以查看我的文章。欢迎讨论!

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up