如何优化 App 的启动耗时?

原文:iOS面试题大全

iOS 的 App 启动主要分为以下步骤:

  • 打开 App,系统内核进行初始化跳转到 dyld 执行。这个过程包括这些步骤:1)分配虚拟内存空间;2)fork 进程;3)加载 MachO (自身所有的可执行 MachO 文件的集合)到进程空间;4)加载动态链接器 dyld 并将控制权交给 dyld 处理。在这个过程中内核会产生 ASLR(Address space layout randomization) 随机数值,这个值用于加载的 MachO 起始地址在内存中的偏移,随机的地址可防止 MachO 代码扫描并被 hack,提升安全性。通过 ASLR 虽然可随机化各内存区基地址,但无法将程序内的代码段和数据段随机化,如果绕过(bypass) ASLR 依然可进行篡改,就需要结合 PIE(Position Independent Executable) 共同使用。与之相似的还有 PIC(Position Independent Code),位置无关代码,作用于共享库代码。PIE/PIC 技术需要在编译阶段开启。顾名思义,PIC 可将程序代码装载到任意地址,这样就内部的指针不能靠固定的绝对地址访问,而通过相对地址指令如 adrp 来获取代码和数据。
  • 进入 dyld 动态链接器,它负责将一个 App 处理为一个可运行的状态,包含:
  • 加载 MachO 的依赖库(这些依赖库也是 MachO 格式的文件)。dyld 从可执行 MachO 文件的依赖开始, 递归加载所有依赖的动态库。 动态库包括:iOS 中用到的所有系统动态库:加载 OC runtime 方法的 libobjc,系统级别的 libSystem(例如 libdispatch(GCD) 和 libsystem_blocks(Block));其他 App 自己的动态库。根据 Apple 的描述,大部分 App 所加载的库在 100~400 个。不过 iOS 系统库已经被特殊优化过,如提前加入共享缓存,提前做好地址修正等。
  • Fix-ups(地址修正),包括 rebasing 和 binding 等。ASLR + PIE 技术增强了程序的安全性,使得依赖固定地址进行攻击的方法失效,但也增加了程序自身的复杂度,MachO 文件的 rebase 和 bind info 等部分以及启动时的 fix-ups 地址修正阶段就是配合它而产生的。
  • ObjC 环境配置。经过了 MachO 程序和依赖库的加载以及地址修正之后,dyld 所做的大部分事情已经完成了。在这一阶段,dyld 开始对主程序的依赖库进行初始化工作,而初始化的执行部分会回调到依赖库内部执行,如 ObjC 的运行时环境所在的 libobjc.A.dylib 以及 libdispatch.dylib 等。ObjC Setup 的过程,主要是对 ObjC 数据进行关联注册:1)dyld 将主程序 MachO 基址指针和包含的 ObjC 相关类信息传递到 libobjc;2)ObjC Runtime 从 __DATA 段中获取 ObjC 类信息,由于 ObjC 是动态语言,可以通过类名获取其实例,所以 Runtime 维护了一个映射所有类的全局类名表。当加载的数据包含了类的定义,类的名字就需要注册到全局表中;3)获取 protocol、category 等类相关属性并与对应类进行关联;4)ObjC 的调用都是基于 selector 的,所以需要对 selector 全局唯一性进行处理。以上步骤由 dyld 启动 libSystem.dylib 统一对基础库进行调用执行,这里面就包含了 libobjc 的 Runtime,同时 Runtime 会在 dyld 绑定回调,当 dyld 处理完相关数据后就会调用 ObjC Runtime 执行 Setup 工作。
  • 执行各模块初始化器。从这一步就开始接近上(业务)层:1)通过 ObjC Runtime 在 dyld 注册的通知,当 MachO 镜像准备完毕后,dyld 会回调到 ObjC 中执行 +load() 方法,包括以下步骤:a)获取所有 non-lazy class 列表;b)按继承以及 category 的顺序将类排入待加载列表;c)对待加载列表中的类进行方法判断并调用 +load() 方法。2)执行 C/C++ 初始化构造器,如通过 attribute((constructor)) 注解的函数。3)如果包含 C++,则 dyld 同样会回调到 libc++ 库中对全局静态变量、隐式初始化等进行调用。
  • 查找并跳转到 main() 函数入口。到了最后,dyld 回到 Load command,找到 LC_MAIN,拿到 entryoff 再加上 MachO 在内存的加载首地址(首地址就是内核传来的 slide 偏移)就得到了 main() 的入口地址,从而进入我们显式的程序逻辑。

进入 main() -> UIApplicationMain -> 初始化回调 -> 显示UI。

iOS 的 App 启动时长大概可以这样计算:

t(App 总启动时间) = t1(main 调用之前的加载时间) + t2(main 调用之后的加载时间)。

t1 = 系统 dylib(动态链接库)和自身 App 可执行文件的加载。

t2 = main 方法执行之后到 AppDelegate 类中的 application:didFinishLaunchingWithOptions:方法执行结束前这段时间,主要是构建第一个界面,并完成渲染展示。

在 t1 阶段加快 App 启动的建议:

  • 尽量使用静态库,减少动态库的使用,动态链接比较耗时。
  • 如果要用动态库,尽量将多个 dylib 动态库合并成一个。
  • 尽量避免对系统库使用 optional linking,如果 App 用到的系统库在你所有支持的系统版本上都有,就设置为 required,因为 optional 会有些额外的检查。
  • 减少 Objective-C Class、Selector、Category 的数量。可以合并或者删减一些 OC 类。
  • 删减一些无用的静态变量,删减没有被调用到或者已经废弃的方法。
  • 将不必须在 +load 中做的事情尽量挪到 +initialize 中,+initialize 是在第一次初始化这个类之前被调用,+load 在加载类的时候就被调用。尽量将 +load 里的代码延后调用。
  • 尽量不要用 C++ 虚函数,创建虚函数表有开销。
  • 不要使用 __atribute__((constructor)) 将方法显式标记为初始化器,而是让初始化方法调用时才执行。比如使用 dispatch_once()pthread_once() std::once()
  • 在初始化方法中不调用 dlopen()dlopen() 有性能和死锁的可能性。
  • 在初始化方法中不创建线程。

在 t2 阶段加快 App 启动的建议:

  • 尽量不要使用 xib/storyboard,而是用纯代码作为首页 UI。
  • 如果要用 xib/storyboard,不要在 xib/storyboard 中存放太多的视图。
  • application:didFinishLaunchingWithOptions: 里的任务尽量延迟加载或懒加载。
  • 不要在 NSUserDefaults 中存放太多的数据,NSUserDefaults 是一个 plist 文件,plist 文件被反序列化一次。
  • 避免在启动时打印过多的 log。
  • 少用 NSLog,因为每一次 NSLog 的调用都会创建一个新的 NSCalendar 实例。
  • 每一段 SQLite 语句都是一个段被编译的程序,调用 sqlite3_prepare 将编译 SQLite 查询到字节码,使用 sqlite_bind_int 绑定参数到 SQLite 语句。
  • 为了防止使用 GCD 创建过多的线程,解决方法是创建串行队列, 或者使用带有最大并发数限制的 NSOperationQueue。
  • 线程安全:UIKit只能在主线程执行,除了 UIGraphics、UIBezierPath 之外,UIImage、CG、CA、Foundation 都不能从两个线程同时访问。
  • 不要在主线程执行磁盘、网络、Lock 或者 dispatch_sync、发送消息给其他线程等操作。

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

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

相关文章

windows qt 不能debug_linux配置vlc-qt

vlc-qt 是基于vlc库,用于开发音频视频应用,性能优秀。vlc-qt/vlc-qt​github.com使用vlc-qt首先需要编译vlc-qt (windows可以下载使用编译好的,但是只能用在release模式)(在windows系统中)使用w…

idou老师教你学Istio 27:解读Mixer Report流程

1、概述 Mixer是Istio的核心组件,提供了遥测数据收集的功能,能够实时采集服务的请求状态等信息,以达到监控服务状态目的。 1.1 核心功能 •前置检查(Check):某服务接收并响应外部请求前,先通过E…

mysql删除密码代码_mysql 用户新建、受权、删除、密码修改

mysql 用户新建、授权、删除、密码修改首先要声明一下:一般情况下,修改MySQL密码,授权,是需要有mysql里的root权限的。注:本操作是在WIN命令提示符下,phpMyAdmin同样适用。用户:phplamp 用户数…

KindEditor 上传漏洞致近百个党政机关网站遭植入

开发四年只会写业务代码,分布式高并发都不会还做程序员? >>> 2月21日消息,近日,安恒明鉴网站安全监测平台和应急响应中心监测发现近百起党政机关网站被植入色情广告页面,分析发现被植入色情广告页面的网站都…

php mysql登陆页面完整代码_求助:PHP实现登陆注册的代码是什么啊(主要是数据库那块)?...

思路:注册:获取前台表单数据->数据库连接->判断数据是否存在,存在输出提示,不存在则向数据库插入表单传来的值->如果sql执行失败输出错误,成功功输出注册成功登陆:获取前台表单数据->数据库连接…

Insql 1.8.2 发布,轻量级 .NET ORM 类库

开发四年只会写业务代码,分布式高并发都不会还做程序员? >>> Insql 是一个轻量级的.NET ORM 类库。对象映射基于 Dapper, Sql 配置灵感来自于 Mybatis。 TA 的追求:简洁、优雅、性能与质量 TA 的宗旨:让你用起来感觉到…

python 函数中所有print保存csv_python for循环print怎样才能输出csv呢

import csv,redef search(req,line):text re.search(req,line)if text:data text.group(1)else:data noreturn datacsvfile file(serp_html.csv,rb)reader csv.reader(csvfile)输出百度搜索结果数据:当前关键词,排名,排名网站&#xff0…

java中具有继承关系的类及其对象初始化顺序

先说结论对于具有继承关系的类,它们的类和对象构造顺序为:父类的类构造器() -> 子类的类构造器() -> 父类成员变量的赋值和实例代码块 -> 父类的构造函数 -> 子类成员变量的赋值和实例代码块 -> 子类的构造函数。 实验代码如下&#xff1…

mysql varchar 2000能存_mysql 数据库 varchar 到底可以存多少数据呢,长文慎入

一、关于UTF-8 UTF-8 Unicode Transformation Format-8bit。是用以解决国际上字符的一种多字节编码。 它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码。 UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。 UTF-8编码…

教程 | 如何利用C++搭建个人专属的TensorFlow

在开始之前,首先看一下最终成型的代码: 分支与特征后端(https://github.com/OneRaynyDay/autodiff/tree/eigen)仅支持标量的分支(https://github.com/OneRaynyDay/autodiff/tree/master)这个项目是我与 Min…

docker kali安装mysql_kali安装docker(有效详细的教程) ——vulhub漏洞复现 001

前记:博主有着多次安装docker的丰富经验,曾经为了在kali成功安装docker花费不少时间。在kali2016.3一直到最新的kali2019.4都通吃!所以跟着下面的步骤走,绝对不会出错。(该机子此前没装过docker,并且配置好了kali更新源…

PDF文件如何转成markdown格式

百度上根据pdf转makrdown为关键字进行搜索,结果大多数是反过来的转换,即markdown文本转PDF格式。 但是PDF转markdown的解决方案很少。 正好我工作上有这个需求,所以自己实现了一个解决方案。 下图是一个用PDF XChange Editor打开的PDF文件&am…

kangle支不支持PHP_【转载】PHP调用kangle的API

摘要:根据管理的API公布写了一个类封装了一个操作集合,这是一个kangleAPI的一个封...根据管理的API公布写了一个类封装了一个操作集合,这是一个kangleAPI的一个封装吧,是在其他地方看到的,接口包含获取easypanel的信息…

ES6 学习笔记(一)let,const和解构赋值

let和const let和const是es6新增的两个变量声明关键字,与var的不同点在于: (1)let和const都是块级作用域,在{}内有效,这点在for循环中非常有用,只在循环体内有效。var为函数作用域。 &#xff0…

mysql数据库容量和性能_新品速递丨容量盘性能提升超 300%,数据库支持 MySQL 8.0...

2关系型数据库 MySQL Plus支持 MySQL 8.0 内核及 XtraBackup 物理在线迁移方式关系型数据库服务 MySQL Plus 发布新版本 1.0.6 , 新增多项功能,提升了集群自动化运维能力。主要升级有:- 支持 MySQL 8.0 内核:根据官方测试&#xf…

10. Python面向对象

Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。如果接触过java语言同学应该都知道,Java面向对象三大特征是:封装、继承、多态。Python面向对象也有一些特征,接下来我…

mysql聚簇索引 和主键的区别_[MySQL] innoDB引擎的主键与聚簇索引

MysqL的innodb引擎本身存储的形式就必须是聚簇索引的形式,在磁盘上树状存储的,但是不一定是根据主键聚簇的,有三种情形:1. 有主键的情况下,主键就是聚簇索引2. 没有主键的情况下,第一个非空null的唯一索引就是聚簇索引3. 如果上面都没有,那么就是有一个隐藏的row-id作为聚簇索引…

前端页面:一直报Cannot set property 'height' of undefined

1、出现错误的例子,只拷贝了项目中关键出现问题的部分 例子中明明写了styleheight:16px这个属性,但是为什么还说height未定义呢 通过打印发现:cks.each(function () { autoTextAreaHeight($(this)); });中的$(this)取出来…

mysql表在线转成分区表_11g普通表在线转换分区表

本帖最后由 灯和树 于 2016-5-4 14:58 编辑由于业务系统数据量增大,对其用户表在线完成分区表转换过程,记录如下,11g数据库支持。创建过渡分区表根据USER_ID创建分区表CREATE TABLE SDP_SMECD.TEST_T_USER_ID(USER_ID NUMBER(13) …

tiger4444/rabbit4444后缀勒索病毒怎么删除 能否百分百恢复

上海某客户中了tiger4444的勒索病毒,找到我们后,一天内全部恢复完成。说了很多关于勒索病毒的事情,也提醒过大家,可总是有人疏忽,致使中招后,丢钱丢面子,还丢工作。 那么要怎样预防呢与处理呢&a…