iOS扩展

iOS APP启动监控和优化思路

2019-03-20  本文已影响151人  梦蕊dream

前言:本文简单描述APP启动过程和监控,一些深入原理性的东西可能需要绕路了,站在大神的肩膀上,简单总结跟APP启动性能有关,如果差错请不吝赐教

冷启动

相对而言冷启动就是App被kill掉以后一切从头开始启动的过程。
App 点击启动前,它的进程不在系统里,需要系统新创建一个进程分配给它启动的情况。这是一次完整的启动过程。
用户感知到的启动慢,其实都发生在主线程上。而主线程慢的原因有很多,比如在主线程上执行了大文件读写操作、在渲染周期中执行了大量计算等。

热启动

当用户按下home键的时候,iOS的App并不会马上被kill掉,还会继续存活若干时间。用户点击App的图标再次回来的时候,App几乎不需要做什么,就可以还原到退出前的状态,继续为用户服务。App 在冷启动后用户将 App 退后台,在 App 的进程还在系统里的情况下,用户重新启动进入 App 的过程。这种持续存活的情况下启动App,称为热启动。

查看APP启动耗时

根据APP启动时间,继续了解APP启动时候都做了哪些
Xcode:(快捷键:command + shift + <)
ProjectSchemeEdit SchemeRunEnvironment Variables
添加 DYLD_PRINT_STATISTICS 环境变量,value为1

环境变量

APP 启动时间:

Total pre-main time: 802.16 milliseconds (100.0%)
         dylib loading time: 294.37 milliseconds (36.6%)
        rebase/binding time: 377.42 milliseconds (47.0%)
            ObjC setup time:  86.68 milliseconds (10.8%)
           initializer time:  43.51 milliseconds (5.4%)
           slowest intializers :
             libSystem.B.dylib :   4.20 milliseconds (0.5%)
    libMainThreadChecker.dylib :  21.22 milliseconds (2.6%)

时间消耗解读

换言之:
App开始启动后,系统首先加载可执行文件(自身App的所有.o文件的集合),然后加载动态链接器dyld,dyld是一个专门用来加载动态链接库的库。 执行从dyld开始,dyld从可执行文件的依赖开始, 递归加载所有的依赖动态链接库。
动态链接库包括:iOS 中用到的所有系统 framework,加载OC runtime方法的libobjc,系统级别的libSystem,例如libdispatch(GCD)和libsystem_blocks (Block)。

APP启动阶段

启动时间:用户点击APP → APP首页面加载完成

main()函数执行前

在 main() 函数执行前,系统主要会做下面几件事情:

程序执行

可优化的功能点

main()函数之后

main()函数开始至 appDelegate
didFinishLaunchingWithOptions结束,称为main()函数之后的部分。

主要执行内容

main()函数之后耗时的影响因素

首屏渲染完成之后

[首屏渲染完成之后]指的是非首屏其他业务服务模块的初始化、监听的注册、配置文件的读取等。
该阶段指的就是截止到 didFinishLaunchingWithOptions 方法作用域内执行首屏渲染之后的所有方法执行完成。从渲染完成时开始,到 didFinishLaunchingWithOptions方法作用域结束时结束。

优化思路一:功能启动优化

main() 函数开始执行后到首屏渲染完成前只处理首屏相关的业务,其他非首屏业务的初始化、监听注册、配置文件读取等都放到首屏渲染完成后去做。

根据刚需分置阶段进行
根据启动流程把刚需功能放置在启动阶段,其他业务功能放在合适的阶段

优化思路二:方法启动优化

检查首屏渲染完成前主线程上的耗时方法,将非刚需的耗时方法滞后或异步执行。耗时较长的方法主要发生在计算大量数据的情况下,例如加载、编辑、存储图片和文件等资源。
+load() 方法,一个耗时 4 毫秒,100 个就是 400 毫秒,不可小视

优化三:移除不必要的动态库

移除项目中非必要的动态库

优化四:移除不必要用到的类

代码工程的维护非常重要

优化五:合并功能相似的类和扩展(Category)

由于Category的实现原理,和ObjC的动态绑定有很强的关系,实际上类的扩展是比较占用启动时间的。尽量可能合并一些扩展,并不是让你不使用扩展

优化六:压缩资源图片

图片小了,IO操作量小了,启动就快了。推荐 TinyPNG

优化七:优化applicationWillFinishLaunching

需要在applicationWillFinishLaunching里处理的业务较多时,可以管理起这些任务
将不需要马上在applicationWillFinishLaunching执行的代码延后执行

优化八:优化rootViewController

rootViewController的加载,适当将某一级的childViewController或subviews延后加载
如果你的App可能会被后台拉起并冷启动,可考虑不加载rootViewController

优化九:小优化

Debug 打印代码块

//MARK: - DEBUG print
func printLog<T>(msg: T,
                 file: String = #file,
                 method: String = #function,
                 line: Int = #line){
    if !DEBUG_ALPHA{//线上环境不print
        return
    }
    print("\((file as NSString).lastPathComponent) \(method),[\(line)]: \(msg)")
}

APP监控方法一:计算主线程方法耗时

定时抓取主线程上的方法调用对战,计算在一段时间内各个方法的耗时。Xcode 工具套件里自带的 Time Profiler,开发类似工具成本不高,能够快速开发后集成到你的 App 中,以便在真实环境中进行检查。

对于定时时间间隔的控制

APP监控方法二:objc_msgSend 方法 hook 所有方法耗时

Hook:在原方法开始执行时换成执行其他指定的方法,或者在原有方法执行前后执行你指定的方法,来达到掌握和改变指定方法的目的。

Objective-C 里每个对象都会指向一个类,每个类都会有一个方法列表,方法列表里的每个方法都是由 selector、函数指针和 metadata 组成的。
objc_msgSend就是在运行时根据对象和方法的 selector 去找到对应的函数指针,然后执行。objc_msgSendObjective-C 里方法执行的必经之路,能够控制所有的 Objective-C 的方法,可自行阅读objc_msgSend 源码获得更深层次的理解。

objc_msgSend 用汇编语言写的:

objc_msgSend 方法执行逻辑
先获取对象对应类的信息,再获取方法的缓存,根据方法的 selector 查找函数指针,经过异常错误处理后,最后跳到对应函数的实现

Tips

Swift AppDelegate 没有main函数入口了

错,swift有main函数,被精简成了一个@NSApplicationMain

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
检查方法耗时的工具

推荐大神神作 SMCallTrace
友情提示:需要在SMCallTrace.m中打开第54行的注释。

+load为什么会增加4毫秒,Swift呢

aop 的耗时,swift没有

iOS中用llvm的IR中插桩来统计函数耗时这个方法也可行

有待学习,嘤嘤嘤

objc的hook是用method swizzle来实现,对于swift

使用Time Profiler 或者 使用Clang打桩统计耗时

oc的代码在编译时会转成c++,再转成c,那swift如何转换?

swift 和 c 编译方式类似。Swift 会先编成 SIL( Swift Intermediate Language)然后再编成机器码。

未完待续…

【干货推荐】
iOS启动时间优化
iOS App 启动性能优化
今日头条iOS客户端启动速度优化
优化 App 的启动时间
《How we cut our iOS app’s launch time in half (with this one cool trick)》
汇编相关
https://blog.nelhage.com/2010/10/amd64-and-va_arg/
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
博客、课程、开源 推荐 戴神

小结:一枚突然顿悟的小白兔,学无止境

上一篇 下一篇

猜你喜欢

热点阅读