持续创作,加快成长!这是我参加「日新计划 10 月更文挑战」的第7天,点击检查活动概况

前言

  • 评价一个App是不是一款出色的应用, 第一印象很重要.
  • 这就要求咱们有必要把App的发动速度的优先级排的很高, 想象一个场景, *点击了App图标, 3秒过去了, 依然还卡在发动状况.*这是令用户难以承受的事.
  • 要想解决问题, 首要要对问题有个全面的知道.
  • 下面咱们就App发动流程及发动优化实操层面, 做一个抛砖引玉的探讨, 如有过错, 请评论区纠正, 先行谢过了:)

1. iOS发动流程剖析

  • App的发动能够分为两种
  1. 冷发动(Cold Launch): 从零开端, 点击App图标发动App
  • 冷发动又能够概括为3大阶段
    • dyld
    • runtime
    • main.m
  1. 热发动(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函数到第一个界面的viewDidAppear.
  • 所以, 优化也是从两个方面进行的, 建议先优化第二部分, 由于绝大多数App的瓶颈在自己的代码里.
    iOS老司机的App启动优化Tips, 让启动速度提升10%

2. App冷发动, 发动优化战略

2.1 mian函数之前的发动优化

2.1.1 什么是dyld?

  • dyld(dynamic link editor), 是Apple的动态连接器, 能够用来装载Mach-O文件(可履行文件、动态库等).
  • App冷发动时, dyld所做的事情有哪些?
  1. 装载App的可履行文件, 同时会递归加载一切依靠的动态库.
  2. 当dyld把可履行文件、动态库都装载完毕后, 会告诉runtime进行下一步的处理.

2.1.2 dyld层面的优化方向

  • 削减动态库的数量
  • 兼并动态库, 比方自己写的UI控件兼并成自己的UIKit
  • 确认动态库是optional还是required.
    • 如果该Framework在当时App支持的一切iOS体系版本都存在,
    • 那么就设为required, 不然就设为optional, 由于option会有些额外的检查.

2.1.3 App冷发动时runtime都做了哪些事?

  1. 调用map_images进行可履行文件内容的解析和处理
  2. load_images中调用call_load_methods, 调用一切ClassCategory+load办法
  3. 进行各种objc结构的初始化如注册Objc类、初始化类目标等
  4. 调用C++静态初始化器和__attribute__((constructor))润饰的函数
  • 到此为止, 可履行文件和动态库中一切的符号(Class、Protocol、Selector、IMP等)都现已按格局成功加载到内存中, 被runtime所管理.

2.1.4 runtime加载层面的优化方向

  • 兼并Category, 如UIView+FrameUIView+AutoLayout兼并成一个.
  • 将不有必要在+load办法中做的事, 放到+initialize中去做.

2.2 main函数之后的发动优化

  • main函数开端履行到显示出第一个页面, 这段时刻做了哪些事?
  1. 履行didFinishLaunchingWithOptions办法
  2. 初始化Window, 初始化根底ViewController
  3. 获取数据
  4. 展现给用户
  • 削减创立线程, 线程不只有创立时的时刻开支, 还会耗费内存, 每个线程大约耗费1kb的内存空间.
    • 线程创立的耗时, 区间范围在4-5毫秒, 创立线程后发动线程的耗时区间为5-100毫秒, 平均在29毫秒.
    • 这是很大的时刻开支, 若在应用发动时敞开多个线程, 则尤为明显. 屡次的上下文切换会带来开支.
    • 在开发中避免滥用多线程.
  • 兼并或许删减不必要的类/分类/函数, 类越多, 函数越多, 发动越慢.
  • 在设计师可承受的范围内, 尽量运用小的图片.

2.3 AppDelegate中的优化

  • 从AppDelegate先下手优化
  • didFinishLaunchingWithOptionsapplicationDidBecomeActive
  • 优化的核心思想便是, 能延时的延时, 不能延时的尽量放到后台去优化.
    • 日志、统计等有必要在App已发动就最先配置的事情, 仍留在didFinishLaunchingWithOptions里发动.
    • 项目配置、环境配置、用户信息的初始化、推送、IM等事情, 这些功用在用户进入App首屏之前是有必要要加载完的, 放到开屏广告页面的viewDidAppear里.
    • 其他SDK和配置事情, 由于发动时刻不是有必要的, 能够放在首屏的viewDidAppear办法里, 在这里不会影响发动时刻.
    • 每次用NSLog方式打印会隐式的创立一个Calendar, 因此需求删减发动时各事务的log, 或许仅针对内测版输出log.
    • 尽量不要在didFinishLaunchingWithOptions里面创立和敞开多线程.

3. 总结

  1. App的发动由dyld主导, 将可履行文件加载到内存, 趁便加载一切依靠的动态库
  2. 并由runtiime负责加载成objc定义的结构
  3. 一切初始化工作完毕后, dyld就会调用main函数
  4. 接下来便是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,坑位有限,备注“网友”可被群管经过~