启动优化——动态库转静态库方案(pre-main时长下降35%)

前文: https://www.jianshu.com/p/31c68ab2d1dd

优化前,对app的第一次安装进行多次测试,发现dylib loading time稳定在700ms左右,pre-main时长在1.2-1.3秒。

Total pre-main time: 1.3 seconds (100.0%)
         dylib loading time: 749.54 milliseconds (53.8%)
        rebase/binding time:  43.28 milliseconds (3.1%)
            ObjC setup time:  31.25 milliseconds (2.2%)
           initializer time: 568.72 milliseconds (40.8%)
           slowest intializers :
             libSystem.B.dylib :   8.06 milliseconds (0.5%)
    libMainThreadChecker.dylib :  43.90 milliseconds (3.1%)
          libglInterpose.dylib : 366.26 milliseconds (26.2%)
                  AFNetworking :  31.09 milliseconds (2.2%)
                     万万商 :  57.11 milliseconds (4.1%)

但是安装后立即进行第二次冷启动,发现dylib loading time下降到200ms以内,pre-main时长在700毫秒左右。

Total pre-main time: 749.32 milliseconds (100.0%)
         dylib loading time: 189.58 milliseconds (25.3%)
        rebase/binding time:   3.44 milliseconds (0.4%)
            ObjC setup time:  16.51 milliseconds (2.2%)
           initializer time: 539.79 milliseconds (72.0%)
           slowest intializers :
             libSystem.B.dylib :   8.64 milliseconds (1.1%)
    libMainThreadChecker.dylib :  43.85 milliseconds (5.8%)
          libglInterpose.dylib : 409.29 milliseconds (54.6%)
                  AFNetworking :  29.30 milliseconds (3.9%)
                     万万商 :  19.24 milliseconds (2.5%)

如果此时,大量打开其他App,使得当前App页表中的页大量被覆盖,再次启动App,会发现加载时长又回到第一次安装的场景。
详细原因《启动优化》有说明:https://www.jianshu.com/p/31c68ab2d1dd

在.app包文件中,可以看到可执行文件大小为36.3MB,.app包文件为110MB。

.app包文件文件过大的原因是内部framewrok文件夹中包含了大量的swift库。因为swift存在大量版本,所以Apple并没有将swift动态库内置到iOS设备中,而是随着App包一起。这也导致了swift语言开发的应用程序大于ObjC。但从Swift 5.0 开始, Apple带来了ABI 稳定.对于 iOS 12.2 的系统,因为它们预装了 Swift 5 的 runtime,所以不再需要 Swift 的库,它们会被从 app bundle 中删掉。


苹果建议开发者自定义的动态库不超过6个,过多的使用导致dylib loading time(动态库加载)时长大量增加。
解决方式有三种:

    1. 动态库转静态库
    1. 合并动态库
    1. 动态库懒加载

下面介绍动态库转静态库方案。
删除.app包文件,在Pod-Target中,将第三方库的Mach-O配置改为 静态库,重新build。
发现.app包文件变成了152MB。



然后进入包看下,可执行文件变成了56MB,这是因为静态库文件在静态链接阶段与可执行文件合并到了一起。



但是发现framework中仍然生成了framework文件,而这个文件是静态库(白色文件代表静态库)。

或者使用file指令,current ar archive 代表是静态库。

% file /Users/sanweiguoye/Library/Developer/Xcode/DerivedData/swg-app-fwwlhopapyyuhrcylltshfsvmwjw/Build/Products/Debug-iphoneos/万万商.app/Frameworks/Rswift.framework/Rswift
/Users/sanweiguoye/Library/Developer/Xcode/DerivedData/swg-app-fwwlhopapyyuhrcylltshfsvmwjw/Build/Products/Debug-iphoneos/万万商.app/Frameworks/Rswift.framework/Rswift: current ar archive

或者使用MachOView查看,多个.o并没有合并成同一个mach-o文件,代表是静态库。


因此,此时静态库在framework多存放了一份,framework静态库,造成包文件变大。

在Pods-swg-app-frameworks.sh文件中删除生成framework的配置,再次build。(同时需要删除旧的.app文件)

  install_framework "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework"
...

可以看到体积下降到105MB。关于静态库的体积压缩,可以通过-force_load配置,这里就不叙述。



第一次安装的时间下降到800ms左右,其中主要是减少了dylib loading time。大量打开其他App导致页面置换,再次打开App,仍然维持在800ms左右。

Total pre-main time: 799.50 milliseconds (100.0%)
         dylib loading time: 154.66 milliseconds (19.3%)
        rebase/binding time:  29.82 milliseconds (3.7%)
            ObjC setup time:  21.75 milliseconds (2.7%)
           initializer time: 593.24 milliseconds (74.2%)
           slowest intializers :
             libSystem.B.dylib :   7.67 milliseconds (0.9%)
    libMainThreadChecker.dylib :  63.65 milliseconds (7.9%)
          libglInterpose.dylib : 394.52 milliseconds (49.3%)
                     万万商 : 141.74 milliseconds (17.7%)

手动更改pod配置的方式,如果重新执行pod install这些配置又会还原,因为在podfile中加入了使用framework!。事实上,cocospod从1.5版本就已经支持swift静态库。

Up until Xcode 9, support for building Swift into static libraries was non-existent and use of dynamic frameworks was required. This was a deal-breaker for some developers, particularly those worried about the launch performance implications of linked many dynamic binaries.
With CocoaPods 1.5.0, developers are no longer restricted into specifying use_frameworks! in their Podfile in order to install pods that use Swift. Interop with Objective-C should just work. However, if your Swift pod depends on an Objective-C, pod you will need to enable "modular headers" (see below) for that Objective-C pod.
https://blog.cocoapods.org/CocoaPods-1.5.0/

如果您的Swift项目依赖于Objective-C pod库,则需要为该Objective-C pod启用"modular headers"。modular是可以直接在Swift中 import 的,不需要再经过 bridging-header 的桥接。

#use_frameworks!
use_modular_headers!
或指定pod
modular_headers => true

如果希望指定部分pod使用静态库,可以在Podfile结尾加上如下代码,将指定库数组的Mach-O 改为静态库。

  staticList = Array['RxBiBinding', 'KeychainAccess']
  post_install do |installer|
      installer.pods_project.targets.each do |target|
          puts "  ||-#{target}"
          staticList.each do |staticTarget|
              if target.name == staticTarget
                  target.build_configurations.each do |config|
                      config.build_settings['MACH_O_TYPE'] = "staticlib"
                  end
              end
          end
      end
  end
动态库转静态库引发的问题
  • 1.符号冲突
  • 2.Bundle的获取

待更新...

你可能感兴趣的:(启动优化——动态库转静态库方案(pre-main时长下降35%))