iOS App的启动与优化

App的启动流程

App启动分为冷启动和热启动

  • 冷启动:从0开始启动App
  • 热启动:App已经在内存中,但是后台还挂着,再次点击图标启动App。

一般对App启动的优化都是针对冷启动。

App冷启动可分为三个阶段:

  1. dyld:加载镜像、动态库
  2. RunTime方法
  3. main函数初始化

动态库vs静态库

静态库:一堆.o文件的集合(通常是.a后缀),还没有被链接过,缺点是产物体积比较大,优点是链接到App之后体积比较小。

动态库:一个已经链接完全的镜像,优点是产物体积比较小,缺点是链接到App之后体积比较大。

两者最大的区别就是静态库没有被链接过,而动态库被链接过。

一.dyld

dyld是app的动态链接器。主要可以用来装载Mach-O文件(可执行文件、动态库等)

启动APP时,dyld所做的事情有:

加载过程从exec()函数开始,这是一个系统调用。操作系统首先为进程分配一段内存空间。然后执行以下操作:

  • 1.把App的可执行文件加载到内存
  • 2.把dyld加载到内存
  • 3.dyld进行动态链接

具体内容:

  • 1、加载动态库
    • Dyld从主执行文件的header获取到需要加载的所依赖动态库列表,然后它需要找到每个 dylib,而应用所依赖的 dylib 文件可能会再依赖其他 dylib,所以所需要加载的是动态库列表一个递归依赖的集合
  • 2、Rebase和Binding
    • 1、Rebase(偏移修正)

             任何一个app生成的二进制文件,在二进制文件内部所有的方法、函数调用,都有一个地址,这个地址是在当前二进制文件中的偏移地址。一旦在运行时刻(即运行到内存中),每次系统都会随机分配一个ASLR(Address Space Layout Randomization,地址空间布局随机化)地址值(是一个安全机制,会分配一个随机的数值,插入在二进制文件的开头),例如,二进制文件中有一个 test方法,偏移值是0x0001,而随机分配的ASLR是0x1f00,如果想访问test方法,其内存地址(即真实地址)变为 ASLR+偏移值 = 运行时确定的内存地址(即0x1f00+0x0001 = 0x1f01)

    • 2、Binding(绑定)

             例如NSLog方法,在编译时期生成的mach-o文件中,会创建一个符号!NSLog(目前指向一个随机的地址),然后在运行时(从磁盘加载到内存中,是一个镜像文件),会将真正的地址给符号(即在内存中将地址与符号进行绑定,是dyld做的,也称为动态库符号绑定),一句话概括:绑定就是给符号赋值的过程

二.RunTime阶段

dyld阶段结束之后就进入RunTime阶段,这个阶段主要进行如下内容:

1、Objc setup

  • 1、注册Objc类 (class registration)
  • 2、把category的定义插入方法列表 (category registration)
  • 3、保证每一个selector唯一 (selector uniquing)

2、Initializers

  • 1、Objc的+load()函数
  • 2、C++的构造函数属性函数
  • 3、非基本类型的C++静态全局变量的创建(通常是类或结构体)

三.main()函数初始化

App的启动由dyld主导,把可执行文件加载到内存,并且加载所有依赖的动态库,并由RunTime负责加载成objc定义的结构,所有初始化工作结束后,dyld就会调用mainn函数 接下来就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法

这个里面往往是最占用启动时间的地方,同时也是我们最为可控的地方。

• 进入 main() 函数,启动应用。

• 执行 UIApplicationMain() 函数,创建 UIApplication 对象并设置 AppDelegate。

• 加载应用的主 UI,包括 storyboard 或 xib 文件,以及 AppDelegate 的各种生命周期方法,如 application:didFinishLaunchingWithOptions:。

四.首屏渲染阶段

初始化rootViewController,加载和渲染界面

渲染完成后,用户将看到应用的首屏。

 

App冷启动流程总结:

1. dyld 加载阶段:

• 动态链接器 dyld 负责加载应用的可执行文件及其依赖的动态库。此时,系统将会做如下工作:

• 查找应用的可执行文件和动态库

• 将它们加载到内存中

• 进行符号解析和绑定

• 执行初始化函数(如 +load 方法和静态构造函数)

2. runtime 初始化阶段:

• ObjC 运行时对类和分类进行注册。

• 执行各类 +load 方法,这个阶段还会进行一些 Swift 类的初始化。

3. main() 函数执行阶段:

• 进入 main() 函数,启动应用。

• 执行 UIApplicationMain() 函数,创建 UIApplication 对象并设置 AppDelegate。

• 加载应用的主 UI,包括 storyboard 或 xib 文件,以及 AppDelegate 的各种生命周期方法,如 application:didFinishLaunchingWithOptions:。

4. 首屏渲染阶段:

• 初始化 rootViewController,加载和渲染界面。

• 渲染完成后,用户将看到应用的首屏。

+load与+initialize

1、+load

(1)+load方法是一定会在runtime中被调用的。只要类被添加到runtime中了,就会调用+load方法,因此+load方法总是在main函数之前调用

(2)+load方法不会覆盖。也就是说,如果子类实现了+load方法,那么会先调用父类的+load方法(无需手动调用super),然后又去执行子类的+load方法。

(3)+load方法只会调用一次。

(4)+load方法执行顺序是:类 -> 子类 ->分类。而不同分类之间的执行顺序不一定,依据在Compile Sources中出现的顺序(先编译,则先调用,列表中在下方的为“先”)。

(5)+load方法是函数指针调用,即遍历类中的方法列表,直接根据函数地址调用。如果子类没有实现+load方法,子类也不会自动调用父类的+load方法。

2、+initialize

(1)+initialize方法是在类或它的子类收到第一条消息之前被调用的,这里所指的消息包括实例方法和类方法的调用。因此+initialize方法总是在main函数之后调用

(2)+initialize方法只会调用一次。

(3)+initialize方法实际上是一种惰性调用,如果一个类一直没被用到,那它的+initialize方法也不会被调用,这一点有利于节约资源。

(4)+initialize方法会覆盖。如果子类实现了+initialize方法,就不会执行父类的了,直接执行子类本身的。如果分类实现了+initialize方法,也不会再执行主类的。

(5)+initialize方法的执行覆盖顺序是:分类 -> 子类 ->类。且只会有一个+initialize方法被执行

(6)+initialize方法是发送消息(objc_msgSend()),如果子类没有实现+initialize方法,也会自动调用其父类的+initialize方法。

3、两者的异同

(1)相同点

  1. load和initialize会被自动调用,不能手动调用它们。
  2. 子类实现了load和initialize的话,会隐式调用父类的load和initialize方法。
  3. load和initialize方法内部使用了锁,因此它们是线程安全的。

(2)不同点

  1. 调用顺序不同,以main函数为分界,+load方法在main函数之前执行,+initialize在main函数之后执行。(存疑)
  2. 子类中没有实现+load方法的话,子类不会调用父类的+load方法;而子类如果没有实现+initialize方法的话,也会自动调用父类的+initialize方法。
  3. +load方法是在类被装在进来的时候就会调用,+initialize在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次,是懒加载模式,如果这个类一直没有使用,就不回调用到+initialize方法。

4、使用场景

(1)+load一般是用来交换方法,由于它是线程安全的,而且一定会调用且只会调用一次,通常在使用UrlRouter的时候注册类的时候也在+load方法中注册。
(2)+initialize方法主要用来对一些不方便在编译期初始化的对象进行赋值,或者说对一些静态常量进行初始化操作。

冷启动时间优化

1.减少动态库(dyld阶段)

一般不多于6个,多余需要进行合并,动态库越多,dyld阶段加载时间越长。

2.减少类和方法的数量

3.延迟初始化(rebase/Binging阶段)

尽量延迟一些不必要的初始化工作,不要在启动时立即初始化所有对象。可以使用懒加载将一些初始化放到用户需要时再进行,以减轻启动阶段的负担。

4. 避免 +load 方法的使用(Initializers阶段)

+load 方法会在 dyld 加载阶段执行,建议用 +initialize 或者在合适的地方延迟执行初始化逻辑,避免阻塞启动流程。

5.优化 AppDelegate(main()阶段)

application:didFinishLaunchingWithOptions: 方法应保持精简,避免在这里进行耗时的操作。将一些耗时任务放到后台队列中异步执行。

6.减少主线程阻塞

启动阶段尽量避免主线程的耗时操作,如文件 I/O、网络请求等。将这些操作放到子线程处理,以免阻塞界面渲染。

7.预编译和瘦身

移除未使用的代码、图片等资源,精简应用的体积,从而减少加载时间。

尽量减少 storyboard 的使用,尤其是大而复杂的 storyboard,可以分解成多个小的 storyboard 或者使用纯代码实现界面。

8.启动时的网络请求

尽量避免在启动时进行同步的网络请求,如果必须请求,可以在启动完成后或在后台进行异步请求,以减少对启动时间的影响。
 

参考:

iOS--App启动过程及优化_ios启动优化-CSDN博客

https://juejin.cn/post/6951591401528229895?searchId=202502182003101C89E818EB9B45117D0A

JHBlog/iOS知识点/iOS大杂烩/APP启动优化/App启动时间优化.md at master · SunshineBrother/JHBlog · GitHub

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/23849.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

StarRocks FE leader节点CPU使用率周期性的忽高忽低问题分析

背景 本文基于 StarRocks 3.3.5 最近在做一些 StarRocks 相关的指标监控的时候,看到了FE master的CPU使用率相对其他FE节点是比较高的,且 呈现周期性的变化(周期为8分钟), 于此同时FE master节点的GC频率相对于其他节…

Spring高级篇-Spring IOC容器 Aware 接口

一、概述 在Spring框架中,IOC(Inversion of Control)容器负责管理应用程序中的对象(即Bean)的生命周期和依赖关系。Spring提供了一系列的Aware接口,允许Bean在初始化时获取Spring容器中的某些资源或信息。…

数字信任的底层逻辑:密码学核心技术与现实应用

安全和密码学 --The Missing Semester of Your CS Education 目录 熵与密码强度密码散列函数密钥体系 3.1 对称加密 3.2 非对称加密信任模型对比典型应用案例安全实践建议扩展练习杂项 密码学是构建数字信任的基石。 本文浅析密码学在现实工具中的应用,涵盖 1&…

MySQL数据库连接池泄露导致MySQL Server超时关闭连接

前言 最近做项目,发现老项目出现xxx,这个错误其实很简单,出现在MySQL数据库Server端对长时间没有使用的client连接执行清楚处理,因为是druid数据库,且在github也出现这样的issue:The last packet successf…

DirectX12(D3D12)基础教程三 线性代数与3D世界空间

线性代数是数学的一个分支,它的研究对象是向量,向量空间(或称线性空间),线性变换和有限维的线性方程组。 向量和矩阵是学习3D入门最基本的理论基础。本章重点讲向量和矩阵. 向量概念 向量最基本的定义就是一个方向和…

LeetCode 230.二叉搜索树中第K小的元素

题目:给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。 思路: 代码: /*** Definition for a binary tree node.* public class Tre…

Android 老项目 jcenter 库失效

最近重新维护了一些老项目发现大部分jcenter库失效了, Could not resolve com.xx:2.1.3. 如果你也遇到了,不妨试试 替换为 aliyun的jcenter服务,就不用一个个找代替库了。 project 下的 build.gradle 文件添加: maven { url htt…

Python数据结构:哈希表-高效存储与查找的秘密武器!

大家周一好!今天我们来聊聊Python中一个非常重要的数据结构——哈希表。无论是算法面试还是实际开发,哈希表都扮演着至关重要的角色。掌握它,你就能轻松解决许多复杂的编程问题! 在编程中,如何实现快速的存储与查找操…

【复习】Redis

数据结构 Redis常见的数据结构 String&#xff1a;缓存对象Hash&#xff1a;缓存对象、购物车List&#xff1a;消息队列Set&#xff1a;点赞、共同关注ZSet&#xff1a;排序 Zset底层&#xff1f; Zset底层的数据结构是由压缩链表或跳表实现的 如果有序集合的元素 < 12…

【电机控制器】ESP32-C3语言模型——DeepSeek

【电机控制器】ESP32-C3语言模型——DeepSeek 文章目录 [TOC](文章目录) 前言一、简介二、代码三、实验结果四、参考资料总结 前言 使用工具&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、简介 二、代码 #include <Arduino.h&g…

STM32-智能小车项目

项目框图 ST-link接线 实物图&#xff1a; 正面&#xff1a; 反面&#xff1a; 相关内容 使用L9110S电机模块 电机驱动模块L9110S详解 | 良许嵌入式 一、让小车动起来 新建文件夹智能小车项目 在里面复制19-串口打印功能 重命名为01-让小车动起来 新建文件夹motor&…

Redis基础学习

目录 Redis命令 通用命令 String Key的顶层格式 Hash List ​编辑​编辑Set SortedSet 在IDEA使用Jedis操作Redis 常规使用 Jedis的连接池 SpringDataRedis 手动序列化和反序列化 操作Hash Redis命令 通用命令 想知道某个命令怎么用 1.可以在官网学习用法 h…

ASP.NET Core Clean Architecture

文章目录 项目地址一、项目主体1. CQRS1.1 Repository数据库接口1.2 GetEventDetail 完整的Query流程1.3 创建CreateEventCommand并使用validation 2. EFcore层2.1 BaseRepository2.2 CategoryRepository2.3 OrderRepository 3. Email/Excel导出3.1 Email1. IEmail接口层2. Ema…

MySQL数据库——表的约束

1.空属性&#xff08;null/not null&#xff09; 两个值&#xff1a;null&#xff08;默认的&#xff09;和not null&#xff08;不为空&#xff09; 数据库默认字段基本都是字段为空&#xff0c;但是实际开发时&#xff0c;尽可能保证字段不为空&#xff0c;因为数据为空没办法…

DeepSeek-R1:通过强化学习激发大语言模型的推理能力

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 DeepSeek大模型技术系列三DeepSeek大模型技术系列三》DeepSeek-…

蓝桥杯备考:递归初阶之汉诺塔问题

我们只要想一个主问题&#xff0c;我们是先把a上面n-1个盘子放在c里&#xff0c;然后再把第n个盘子放在b上&#xff0c;再利用a把c上n-1个盘子都放在b上就行了 #include <iostream> using namespace std;void dfs(int n,char x,char y,char z) {if(n0) return;dfs(n-1,x…

聊一聊vue如何实现角色权限的控制的

大家好&#xff0c;我是G探险者。 关于角色与权限控制&#xff0c;通常是分为两大类&#xff1a;一种是菜单权限&#xff1b;一种是操作权限。 菜单权限是指&#xff0c;每个角色对应着可以看到哪些菜单&#xff0c;至于每个菜单里面的每个按钮&#xff0c;比如增删改查等等这类…

如何将公钥正确添加到服务器的 authorized_keys 文件中以实现免密码 SSH 登录

1. 下载密钥文件 2. RSA 解析 将 id_ed25519 类型的私钥转换为 RSA 类型&#xff0c;要将 ED25519 私钥转换为 RSA 私钥&#xff0c;需要重新生成一个新的 RSA 密钥对。 步骤&#xff1a; 生成新的 RSA 密钥对 使用 ssh-keygen 来生成一个新的 RSA 密钥对。比如&#xff0c;执…

RK Android11 WiFi模组 AIC8800 驱动移植流程

RK Android WiFi模组 AIC8800 驱动移植流程 作者&#xff1a;Witheart更新时间&#xff1a;20250220 概要&#xff1a;本文介绍了基于 AIC8800D40 芯片的 WiFi6 模组 BL-M8800DS2-40 在 RK3568 平台上的驱动移植流程。主要涉及环境搭建、驱动代码分析、设备树修改、驱动编译配…

力扣3102.最小化曼哈顿距离

力扣3102.最小化曼哈顿距离 题目 题目解析及思路 题目要求返回移除一个点后的最小的最大曼哈顿距离 最大最小值的题一般直接想到二分 本题有一个简单办法就是利用切比雪夫距离 当正方形转45&#xff0c;即边上点**( x , y ) -> (x y , y - x)时&#xff0c;两点间max(…