🐻记录启动速度优化30%的过程

5,748 阅读5分钟

我曾在掘金上发布过一个沸点,关于使用use_frameworks! :linkage => :static的困惑,也在技术群里询问过、同时问了一些小伙伴,不过没能获得自己想要的答案。我为此查阅了很多资料,总算是能解释通了。

包变小的原因:www.jianshu.com/p/03aac1412…

目的

优化App的启动速度时,阅读了很多优秀的文章,同时也发现有些文章表述有问题,甚至会影响开发者的思维,带入一个误区,比如静态库、动态库的这个概念,因此,总结下,以免读者百思不得其解。利用Cocoapods新特性提高30%的启动速度,见本文最后。

结论

  • 我们自己的Framework 都是 伪Framework,只有官方的是 真Framework

  • 通过Pod引入的Framework可以是静态的,也可以是动态的。和设置有关,你可以手动更改,在Build Settings => Mach-O Type 改为 Static Library:那么就是静态的了,默认是Dynamic。

  • 如果这个库不是App 和 Extension共享的,那么请尽可能的使用静态库,这样不仅会加快启动速度,还会优化包大小。

Extension 和 App 是两个分开的可执行文件,同时需要共享代码,这种情况下动态库的支持就是必不可少的了。但是这种动态 Framework 和系统的 UIKit.Framework 还是有很大区别。系统的 Framework 不需要拷贝到目标程序中,我们自己做出来的 Framework 哪怕是动态的,最后也还是要拷贝到 App 中(App 和 Extension 的 Bundle 是共享的),因此苹果又把这种 Framework 称为 Embedded Framework。

动态库与静态库区别

什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用。

什么时候我们会用到库呢?一种情况是某些代码需要给别人使用,但是我们不希望别人看到源码,就需要以库的形式进行封装,只暴露出头文件。另外一种情况是,对于某些不会进行大的改动的代码,我们想减少编译的时间,就可以把它打包成库,因为库是已经编译好的二进制了,编译的时候只需要 Link 一下,不会浪费编译时间。

上面提到库在使用的时候需要 Link,Link 的方式有两种,静态和动态,于是便产生了静态库和动态库。

原文:segmentfault.com/a/119000000… ---- 作者:skyline75489

Swift 不利因素

Swift 虽然在 5.0 版本完成了 ABI 稳定,但是在低版本操作系统中 (iOS 12.2 以下) 仍旧会有一些不够完美的地方。

在低于 iOS12.2 以下的操作系统会带来相应的包大小增加负担,因为Swift1.x,2.x,3.x,API不稳定,很多东西没办法放到系统更新安装包中,只能把Swift运行时打包进自己的App中,因此会增大一部分。

假如你的手机系统是ios10,假如把当时最新的Swift2.x的运行库放到系统中,过了几个月swift3.x出来了,有人用swift3.x写了个软件,你去安装它,由于别人是swift3.x,但你只有Swift2.x的运行库,那怎么办,所以苹果被逼无奈,只能报把运行支持库打包到app中,因此,理论上会有这部分的增加。

在低于 iOS12.2 以下的操作系统会有 100-200ms不等的启动耗时增加,因为使用的是动态库,运行时链接。

摘抄:mp.weixin.qq.com/s/oHGkoGzhM…

利用Cocoapods1.9新特性提高30%的启动速度,包大小减少10MB

在Podfile将use_frameworks!修改为:

use_frameworks! :linkage => :static

可以提高30%的启动速度,包大小减少10MB(与使用的动态库数量与类型有关)。

CocoaPods 1.9 Beta已经到来!

即刻团队早期的文章可验证

动态库你自己的,共享给谁用?莫非你的Alamofire还能共享给其他app?显然动态库有点浪费,不如静态库实在,能用静态库就用静态库,除非。。。

我问了好多人:大家都说改成静态库,包大小会加大,我之前也深信不疑,直到我发现无法解释时,才追根溯源。

如果你有不同观点。评论区留言。或入群battle。

二进制重排

第一步:安装 AppOrderFiles(使用pod)

post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['ARCHS'] = 'arm64'
            config.build_settings['OTHER_CFLAGS'] = '-fsanitize-coverage=func,trace-pc-guard'
            config.build_settings['OTHER_SWIFT_FLAGS'] = '-sanitize-coverage=func -sanitize=undefined'
        end
    end
end

第二步:增加Flags

Other C Flags 中增加 -fsanitize-coverage=func,trace-pc-guard Other Swift Flags 中增加 -sanitize-coverage=func 与 -sanitize=undefined

第三步:在viewdidAppera中增加(首屏渲染结束后)

AppOrderFiles { (path) in XHPrint("quinn---- (path)") }

运行App,获取到函数调用顺序的文件。path 表示路径

第四步:在 xcode中搜索 order file 增加配置,把第三步生成的文件,拷贝到工程中,并引用

第五步,删除 AppOrderFiles以及先关代码,进行测试。

AppOrderFiles 利用 Clang SanitizerCoverage

简单来说 SanitizerCoverage 是 Clang 内置的一个代码覆盖工具。它把一系列以 _sanitizer_cov_trace_pc 为前缀的函数调用插入到用户定义的函数里,借此实现了全局 AOP 的大杀器。其覆盖之广,包含 Swift/Objective-C/C/C++ 等语言,Method/Function/Block 全支持。

开启 SanitizerCoverage 的方法是:在 build settings 里的 “Other C Flags” 中添加 -fsanitize-coverage=func,trace-pc-guard。如果含有 Swift 代码的话,还需要在 “Other Swift Flags” 中加入 -sanitize-coverage=func 和 -sanitize=undefined。所有链接到 App 中的二进制都需要开启 SanitizerCoverage,这样才能完全覆盖到所有调用。

地址:github.com/yulingtianx…