抖音品质建设 - iOS启动优化《实战篇》(1)

优化的整体思路其实就四步:

  1. 掉启动项,最直接

  2. 如果不能删除,尝试延迟,延迟包括第一次访问以及启动结束后找个合适的时间预热

  3. 不能延迟的可以尝试并发,利用好多核多线程

  4. 如果并发也不行,可以尝试让代码执行更快

这块会以 Main 函数做分界线,看下 Main 函数前后的优化方案;接着介绍如何优化 Page In;最后讲解一些非常规的优化方案,这些方案对架构的要求比较高

Main 之前


Main 函数之前的启动流程如下:

  • 加载 dyld

  • 创建启动闭包(更新 App/重启手机需要)

  • 加载动态库

  • Bind & Rebase & Runtime 初始化

  • +load 和静态初始化

动态库

减少动态库数量可以加减少启动闭包创建和加载动态库阶段的耗时,官方建议动态库数量小于 6 个。

推荐的方式是动态库转静态库,因为还能额外减少包大小。另外一个方式是合并动态库,但实践下来可操作性不大。最后一点要提的是,不要链接那些用不到的库(包括系统),因为会拖慢创建闭包的速度。

下线代码

下线代码可以减少 Rebase & Bind & Runtime 初始化的耗时。那么如何找到用不到的代码,然后把这些代码下线呢?可以分为静态扫描和线上统计两种方式

最简单的静态扫描是基于 AppCode,但是项目大了之后 AppCode 的索引速度非常慢,另外的一种静态扫描是基于 Mach-O 的:

  • _objc_selrefs_objc_classrefs 存储了引用到的 sel 和 class

  • __objc_classlist 存储了所有的 sel 和 class

二者做个差集就知道那些类/sel 用不到,但objc 支持运行时调用,删除之前还要在二次确认

还有一种统计无用代码的方式是用线上的数据统计,主流的方案有三种:

  • ViewConteroller 渗透率,hook 对应的声明周期方法即可统计

  • Class 渗透率,遍历运行时的所有类,通过 Objective C Runtime 的标志位判断类是否被访问

  • 行级渗透率,需要用编译期插桩,对包大小和执行速度均有损。

前两种是 ROI 较高的方案,绝大多数时候 Class 级别的渗透率足够了。

+load 迁移

+load 除了方法本身的耗时,还会引起大量 Page In,另外 +load 的存在对 App 稳定性也是冲击,因为 Crash 了捕获不到。

举个例子,很多 DI 的容器需要把协议绑定到类,所以需要在启动的早期(+load)里注册:

+ (void)load

{

[DICenter bindClass:IMPClass toProtocol:@protocol(SomeProcotol)]

}

本质上只要知道协议和类的对应关系即可,利用 clang attribute,这个过程可以迁移到编译期:

typedef struct{

const char * cls;

const char * protocol;

}_di_pair;

#if DEBUG

#define DI_SERVICE(PROTOCOL_NAME,CLASS_NAME)\

__used static Class<PROTOCOL_NAME> _DI_VALID_METHOD(void){\

return [CLASS_NAME class];\

}\

__attribute((used, p(_DI_SEGMENT “,” _DI_SECTION ))) static _di_pair _DI_UNIQUE_VAR = \

{\

_TO_STRING(CLASS_NAME),\

_TO_STRING(PROTOCOL_NAME),\

};\

#else

__attribute((used, p(_DI_SEGMENT “,” _DI_SECTION ))) static _di_pair _DI_UNIQUE_VAR = \

{\

_TO_STRING(CLASS_NAME),\

_TO_STRING(PROTOCOL_NAME),\

};\

#endif

原理很简单:宏提供接口,编译期把类名和协议名写到二进制的指定段里,运行时把这个关系读出来就知道协议是绑定到哪个类了

有同学会注意到有个无用的方法_DI_VALID_METHOD ,这个方法只在 debug 模式下存在,为了让编译器保证类型安全。

静态初始化迁移

静态初始化和 +load 方法一样也会引起大量 Page In,一般来自 C++代码,比如网络或者特效的库。另外有些静态初始化是通过头文件引入进来的,可以通过预处理来确认。

几个典型的迁移思路:

  • std:string 转换成 const char

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值