持续创作,加快成长!这是我参加「日新计划 10 月更文挑战」的第7天,点击检查活动概况
前言
- 评价一个App是不是一款出色的应用, 第一印象很重要.
- 这就要求咱们有必要把App的发动速度的优先级排的很高, 想象一个场景, *点击了App图标, 3秒过去了, 依然还卡在发动状况.*这是令用户难以承受的事.
- 要想解决问题, 首要要对问题有个全面的知道.
- 下面咱们就App发动流程及发动优化实操层面, 做一个抛砖引玉的探讨, 如有过错, 请评论区纠正, 先行谢过了:)
1. iOS发动流程剖析
- App的发动能够分为两种
- 冷发动(Cold Launch): 从零开端, 点击App图标发动App
- 冷发动又能够概括为3大阶段
-
- dyld
-
- runtime
-
- main.m
- 热发动(Warm Launch): App之前现已发动好了, 在内存中, 后台运转, 再次点击App图标发动App
- App发动时刻的优化, 主要是针对冷发动进行.
1.1 打印检查当时App的发动时刻
- 能够经过增加XCode环境变量打印出App的发动耗时
-
Edit scheme -> Run -> Arguments ,
DYLD_PRINT_STATISTICS
设置为1
.
1.2 App冷发动, 流程剖析
- 发动时刻是用户点击App图标, 到第一个界面展现的时刻.
- 以main函数作为分水岭, 发动时刻其实包含了两部分,
-
- main函数之前, 剖析并加载动态库, 注册需求的类, Category中的办法也会注册到对应的类中, 履行必要的初始化
+load
办法
- main函数之前, 剖析并加载动态库, 注册需求的类, Category中的办法也会注册到对应的类中, 履行必要的初始化
-
- main函数到第一个界面的
viewDidAppear
.
- main函数到第一个界面的
- 所以, 优化也是从两个方面进行的, 建议先优化第二部分, 由于绝大多数App的瓶颈在自己的代码里.
2. App冷发动, 发动优化战略
2.1 mian函数之前的发动优化
2.1.1 什么是dyld?
- dyld(dynamic link editor), 是Apple的动态连接器, 能够用来装载
Mach-O
文件(可履行文件、动态库等). - App冷发动时, dyld所做的事情有哪些?
- 装载App的可履行文件, 同时会递归加载一切依靠的动态库.
- 当dyld把可履行文件、动态库都装载完毕后, 会告诉
runtime
进行下一步的处理.
2.1.2 dyld层面的优化方向
- 削减动态库的数量
- 兼并动态库, 比方自己写的UI控件兼并成自己的UIKit
- 确认动态库是
optional
还是required
. -
- 如果该Framework在当时App支持的一切iOS体系版本都存在,
-
- 那么就设为
required
, 不然就设为optional
, 由于option
会有些额外的检查.
- 那么就设为
2.1.3 App冷发动时runtime都做了哪些事?
- 调用
map_images
进行可履行文件内容的解析和处理 - 在
load_images
中调用call_load_methods
, 调用一切Class
和Category
的+load
办法 - 进行各种
objc
结构的初始化如注册Objc类、初始化类目标等 - 调用C++静态初始化器和
__attribute__((constructor))
润饰的函数
- 到此为止, 可履行文件和动态库中一切的符号(Class、Protocol、Selector、IMP等)都现已按格局成功加载到内存中, 被
runtime
所管理.
2.1.4 runtime加载层面的优化方向
- 兼并Category, 如
UIView+Frame
和UIView+AutoLayout
兼并成一个. - 将不有必要在
+load
办法中做的事, 放到+initialize
中去做.
2.2 main函数之后的发动优化
- main函数开端履行到显示出第一个页面, 这段时刻做了哪些事?
- 履行
didFinishLaunchingWithOptions
办法 - 初始化Window, 初始化根底ViewController
- 获取数据
- 展现给用户
- 削减创立线程, 线程不只有创立时的时刻开支, 还会耗费内存, 每个线程大约耗费1kb的内存空间.
-
- 线程创立的耗时, 区间范围在4-5毫秒, 创立线程后发动线程的耗时区间为5-100毫秒, 平均在29毫秒.
-
- 这是很大的时刻开支, 若在应用发动时敞开多个线程, 则尤为明显. 屡次的上下文切换会带来开支.
-
- 在开发中避免滥用多线程.
- 兼并或许删减不必要的类/分类/函数, 类越多, 函数越多, 发动越慢.
- 在设计师可承受的范围内, 尽量运用小的图片.
2.3 AppDelegate中的优化
- 从AppDelegate先下手优化
-
didFinishLaunchingWithOptions
、applicationDidBecomeActive
- 优化的核心思想便是, 能延时的延时, 不能延时的尽量放到后台去优化.
-
- 日志、统计等有必要在App已发动就最先配置的事情, 仍留在
didFinishLaunchingWithOptions
里发动.
- 日志、统计等有必要在App已发动就最先配置的事情, 仍留在
-
- 项目配置、环境配置、用户信息的初始化、推送、IM等事情, 这些功用在用户进入App首屏之前是有必要要加载完的, 放到开屏广告页面的
viewDidAppear
里.
- 项目配置、环境配置、用户信息的初始化、推送、IM等事情, 这些功用在用户进入App首屏之前是有必要要加载完的, 放到开屏广告页面的
-
- 其他SDK和配置事情, 由于发动时刻不是有必要的, 能够放在首屏的
viewDidAppear
办法里, 在这里不会影响发动时刻.
- 其他SDK和配置事情, 由于发动时刻不是有必要的, 能够放在首屏的
-
- 每次用NSLog方式打印会隐式的创立一个
Calendar
, 因此需求删减发动时各事务的log, 或许仅针对内测版输出log.
- 每次用NSLog方式打印会隐式的创立一个
-
- 尽量不要在
didFinishLaunchingWithOptions
里面创立和敞开多线程.
- 尽量不要在
3. 总结
- App的发动由
dyld
主导, 将可履行文件加载到内存, 趁便加载一切依靠的动态库 - 并由
runtiime
负责加载成objc
定义的结构 - 一切初始化工作完毕后,
dyld
就会调用main
函数 - 接下来便是
UIApplictionMain
函数,AppDelegate
的
3.1 dyld阶段
- 削减动态库、兼并一些动态库定时整理不必要的动态库
- 削减
Objc
类、分类的数量、削减Selector
数量, 定时整理不必要的类、分类 - 削减C++虚函数的数量
3.2 runtime阶段
- 用
+initialeze
办法和dispatch_once
替代一切的__attribute__((constructor))
、C++静态构造器、Objc的+load
办法
3.3 main函数阶段
- 在不影响用户体会的前提下, 尽可能将一些初始化操作推迟, 不要全部都放在
finishLaunching
办法中, 做到推迟加载, 按需加载.
发文不易, 喜欢点赞的人更有好运气 :), 定时更新+关注不迷路~
ps:欢迎加入笔者18年树立的研讨iOS审核及前沿技术的三千人扣群:662339934,坑位有限,备注“网友”可被群管经过~