美文网首页
App启动耗时优化

App启动耗时优化

作者: 格雷s | 来源:发表于2020-04-28 19:58 被阅读0次

    1.App启动流程分析

    iOS App启动时,系统会调用fork和execve,fork功能创建app进程,execve加载和运行程序,然后进入Pre-main阶段,然后进入applicationDidFinishLaunching:,下面我们梳理下app启动到展示的过程:
    启动app->初始化空间,创建进程->加载解析执行文件exec_activate_image->载入动态链接器load_dylinker->配置用户栈等环境->设置线程入口->加载依赖的共享库->查找并绑定符号->恢复app的入口->mian()->UIapplicationMain()—>UIapplication->appdelegate->loads info.plist->creates and manages runloop->sends applications:didFinishLaunchingWithOptions:->creates and displays

    2.Pre-main优化

    通过配置Environment Variables参数DYLD_PRINT_STATISTICS:1,可以查看Pre-main时间,我们先查看下优化之前的数据

    Total pre-main time: 2.2 seconds (100.0%)
             dylib loading time: 1.1 seconds (51.2%)
            rebase/binding time: 101.93 milliseconds (4.4%)
                ObjC setup time:  31.59 milliseconds (1.3%)
               initializer time: 981.14 milliseconds (42.8%)
               slowest intializers :
              libglInterpose.dylib : 422.59 milliseconds (18.4%)
                      AFNetworking : 236.77 milliseconds (10.3%)
    ...
    

    可以看到时间主要花在dylib loadingrebase/bindingObjC setupinitializer
    下面我们分析下怎样去优化这几个阶段的时间消耗

    • dylib loading
      动态库加载,在这个阶段,系统的动态库做了高度优化,自定义动态库则需要花费更多的时间,所以合理使用自定义动态库资源
    • rebase/binding
      加载到内存的可执行文件都是不可用的状态,因为有ASLR的存在,在虚拟内存中的地址都是随机的,所以需要fix-ups,主要两个步骤:rebase ->binding
      rebase:因为初始地址和内存地址不同,需要修正.
      binding:因为动态库不编译进程序最终的二进制文件中,而是在运行的时候动态的查找调用函数的地址,调用外部符号进行绑定的过程就称作binding.
      以上两个步骤主要针对的就是__DATA中的指针数量,所以这个阶段需要优化方法
    • ObjC setup
      数据修正后将会注册objc类,如果有分类,还需要将分类定义的方法插入的方法列表中
      Objc setup主要是在objc_init完成的,objc_init是在libsystem中的initialize方法libsystem_initializer中初始化了libdispatch,然后,libdispatch_init调用了os_object_init,最终调用了objc_init,runtime在objc_init中绑定了3个方法,map_2_images,load_images,unmap_images.
      • map_2_images:Binding操作结束之后,发出dyld_image_state_bound通知,调用map_2_images,主要做以下几件事来完成objc setup:
        -- 读取二进制文件的DATA段内容,找到与objc相关的信息.
        -- 注册objc类
        -- 确保selector的唯一性
        -- 读取protocol以及category的信息
      • load_images:函数作用就是调用objc的load方法,它监听dyld_image_state_dependents-initialize通知
      • unmap_image可以理解为map_2_images的逆向操作.
        以上3步都是修改__DATA segment中的内容,所以这个阶段需要优化load分类

    经过以上几个步骤的初步优化,比如移除冗余文件、冗余方法、load优化、分类优化,我们再看看pre-main消耗

    Total pre-main time: 1.5 seconds (100.0%)
             dylib loading time: 733.71 milliseconds (46.0%)
            rebase/binding time:  91.76 milliseconds (5.7%)
                ObjC setup time:  28.08 milliseconds (1.7%)
               initializer time: 738.51 milliseconds (46.3%)
               slowest intializers :
              libglInterpose.dylib : 375.18 milliseconds (23.5%)
                      AFNetworking :  57.57 milliseconds (3.6%)
    ...
    

    可以看到pre-main减少0.7s,效果还是比较不错的

    3. applicationDidFinishLaunching优化

    本片文章并不是讲冷启动的耗时优化,而是在进入applicationDidFinishLaunching方法到rootViewController展示的过程,为了尽快让用户能看到app首页,我们需要尽量缩短applicationDidFinishLaunching方法的耗时,尽快加载出rootViewController数据,为此,我们从Demo中来看看,applicationDidFinishLaunching耗时到底能否影响rootViewController的展示

    首先在app入口处我们获取启动时间


    image.png

    在rootViewController中获取appear时间


    image.png
    查看结果:
    image.png

    然后我们在applicationDidFinishLaunching中插入一个耗时操作


    image.png

    查看结果


    image.png
    可以看到,applicationDidFinishLaunching在主线程中耗时越多,rootViewController出现的就越慢

    为了提升用户体验,缩短app启动白屏等待的时间,我们可以尽量将耗时操作延迟执行、异步子线程执行、优化耗时时间等

    4.结合Runloop管理启动项

    • 在做App优化的过程中,我设计了一个基于runloop的任务管理器,利用runloop的空闲状态执行任务GLRunloopTaskTool
    • 我们可以将需要执行,但是又不是必须立即执行的任务用这个工具管理,比如某些UI渲染、某些数据加载等等

    5.Instruments之App Launch

    image.png

    在xcode11之后,Instruments中多了一个App Launch,这个在优化分析app启动过程有非常直观的作用

    image.png
    • 运行该工具,会启动app并统计5s内app启动过程中所做的事情,之后解析数据,上图是我运行App Launch的一次分析结果

    • 我们选中.app,选中Time Profile,这样可以看到不同阶段的统计,我们重点关注下绿色部分Launching过程,该部分由以下几个部分组成:

      • Launching-UIKit initialization
      • didFinishLaunchingWithOptions()
      • UIKit Scene Creation
      • initial Frame Rendering
        即:相关UIKit初始化、DidFinishLaunchingWithOptions执行,创建初始画面,这正是我们优化app启动过程中重点关注的阶段
    • 3连击其中一个阶段可以,可以看到该阶段所调用的方法及耗时,还可以勾选Call Tree中的,Separate by ThreadHide System Libraries方便查看

      image.png
    • App启动优化工作和App瘦身工作,在代码优化方面有很多重合工作,在App瘦身工作落地后,App启动优化阶段,我主要做了didFinishLaunchingWithOptionsinitial Frame Rendering优化,release环境下调试

    • App Launch统计的Launching阶段数据来看,从之前的655ms优化到了245ms

    • 启动阶段总的MainThread耗时从1.16s优化到了0.54s

    优化前 优化后

    相关文章

      网友评论

          本文标题:App启动耗时优化

          本文链接:https://www.haomeiwen.com/subject/jqbbwhtx.html