热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS底层探索(二十五)启动优化

iOS底层探索(二十五)启动优化冷启动:在内存中不包含相关的数据,由系统决定热启动:在杀掉应用后࿰

iOS 底层探索(二十五) 启动优化

冷启动:在内存中不包含相关的数据,由系统决定
热启动:在杀掉应用后,内存数据依然在

测试App启动,用main函数做分界点,main函数之前叫pre-main,main函数之前的时间是系统决定的,启动时间不能统计,main函数之后的启动时间是可以启动的。由于在main之后的操作在每个App都会有所不同。


配置系统启动时间打印

Arguments中添加DYLD_PRINT_STATISTICS选项
在这里插入图片描述

运行查看结果
在这里插入图片描述

因为当前项目是个空项目,没有参考性,因此我们需要借助一个完整的项目进行重签名查看。
在项目的根目录下,创建App文件夹,将.ipa文件拷入
在这里插入图片描述

创建App签名的appSign.sh文件,与App文件夹同级,内容如下

# ${SRCROOT} 它是工程文件所在的目录
TEMP_PATH="${SRCROOT}/Temp"
#资源文件夹,我们提前在工程目录下新建一个APP文件夹,里面放ipa包
ASSETS_PATH="${SRCROOT}/App"
#目标ipa包路径
TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
#清空Temp文件夹
rm -rf "${SRCROOT}/Temp"
mkdir -p "${SRCROOT}/Temp"#----------------------------------------
# 1. 解压IPA到Temp下
unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
# 拿到解压的临时的APP的路径
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
# echo "路径是:$TEMP_APP_PATH"#----------------------------------------
# 2. 将解压出来的.app拷贝进入工程下
# BUILT_PRODUCTS_DIR 工程生成的APP包的路径
# TARGET_NAME target名称
TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
echo "app路径:$TARGET_APP_PATH"rm -rf "$TARGET_APP_PATH"
mkdir -p "$TARGET_APP_PATH"
cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"#----------------------------------------
# 3. 删除extension和WatchAPP.个人证书没法签名Extention
rm -rf "$TARGET_APP_PATH/PlugIns"
rm -rf "$TARGET_APP_PATH/Watch"#----------------------------------------
# 4. 更新info.plist文件 CFBundleIdentifier
# 设置:"Set : KEY Value" "目标文件路径"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"#----------------------------------------
# 5. 给MachO文件上执行权限
# 拿到MachO文件的路径
APP_BINARY&#61;&#96;plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<&#96;
#上可执行权限
chmod &#43;x "$TARGET_APP_PATH/$APP_BINARY"#----------------------------------------
# 6. 重签名第三方 FrameWorks
TARGET_APP_FRAMEWORKS_PATH&#61;"$TARGET_APP_PATH/Frameworks"
if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
then
for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
do#签名
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
done
fi#注入
#yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/HankHook.framework/HankHook"

编译配置&#xff0c;添加配置选项
在这里插入图片描述

运行查看打印内容&#xff0c;如下
在这里插入图片描述


  • 当前项目整体耗时1.8s

  • dylib loading&#xff1a;加载动态库的耗时&#xff0c;官方建议自定义的动态库数量为6个&#xff0c;从包内部可知我们加载的包的动态库也是6个&#xff0c;如果多于6个&#xff0c;就需要考虑动态库合并了。
    在这里插入图片描述

  • rebase/binding&#xff1a;rebase&#xff1a;偏移修正&#xff0c;binding&#xff1a;符号绑定

    • rebase&#xff1a;偏移修正&#xff0c;任何一款App生成的二进制文件&#xff0c;在二进制文件的方法函数调用都会存在地址&#xff0c;这个地址是二进制文件的偏移地址。一旦在运行时刻&#xff0c;系统会给二进制文件的头部加入ASLR(安全机制&#xff0c;分配一个随机值)&#xff0c;因此在运行时刻&#xff0c;方法的地址会存在偏移&#xff0c;因此需要偏移修正
    • binding&#xff1a;绑定&#xff0c;例如在方法内做NSLog打印时&#xff0c;在编译中就会在MatchO文件中&#xff0c;创建一个符号叫做NSLog。当运行时进行地址关联&#xff0c;即关联到NSLog函数真正的地址&#xff0c;这个关联的过程叫做绑定。如果想缩短这个时间&#xff0c;处理方法就是少用一点外部的库
  • ObjC setup&#xff1a;OC类的耗时&#xff0c;即OC的类越&#xff0c;就越耗时&#xff0c;相比swift来说&#xff0c;swift性能要好一点&#xff0c;优化方法删除不用的类以及方法&#xff0c;因为他会参与编译。

  • initializer&#xff1a;load和C&#43;&#43;的构造函数的耗时

    • WeChat&#xff1a;是主程序的耗时&#xff0c;其他均为系统的耗时

优化方案


  1. 启动时刻的页面最好使用代码&#xff0c;而不是xib以及storyboard
  2. 影响启动的耗时操作&#xff0c;尽量使用多线程
  3. 删除不用的以及方法&#xff0c;因为他会参与编译。

二进制重排

虚拟内存物理内存
早期的计算机没有虚拟内存的概念&#xff0c;都是物理内存。在早期的计算机中&#xff0c;如果打开的软件过多时&#xff0c;会出现内存不足的弹窗警告。这是因为在应用程序运行时&#xff0c;需要将应用程序加载到内存中&#xff0c;然后再进行加载&#xff0c;但是物理内存空间有限&#xff0c;一旦你打开的应用过多时&#xff0c;就会出现内存不足的警告。
在这里插入图片描述

问题


  1. 内存不够用&#xff0c;即应用软件的发展速度远比硬件的发展速度快
    • 软件越来越大后&#xff0c;用户使用这款软件的功能可能是部分功能&#xff0c;因此将应用全部载入内存是一种物理内存的浪费
  2. 内存数据安全问题&#xff0c;以前的游戏外挂中进行内存搜索时&#xff0c;可以就行内存的数据修改&#xff0c;绕过操作系统的阻碍。

虚拟内存

让应用程序以为载入了内存&#xff0c;又没有加载到真正的物理内存中。由此引发虚拟内存。
在这里插入图片描述

虚拟内存流程


  1. 系统将应用程序进行分页&#xff0c;将应用加入到虚拟内存地址&#xff0c;每个应用的虚拟内存都是独立的。
  2. 物理内存中只有正在活跃的应用程序的某一个部分
  3. 当访问数据时&#xff0c;CPU首先访问虚拟内存&#xff0c;从虚拟地址与物理地址对应关系查看物理内存中是否有该虚拟地址的内容&#xff0c;如果有则直接使用&#xff0c;如果没有则重新载入物理内存中&#xff0c;然后进行访问
  4. 这样做可以节省内存空间。
  5. 这样解决了内存不够用以及安全问题&#xff0c;因为应用程序只能访问虚拟内存&#xff0c;无法直接访问物理内存。
  6. 将应用程序以页来分可以方便管理&#xff0c;并方便虚拟内存映射。在Linux、macOS系统中一页是4K&#xff0c;iOS系统中一页是16K
  7. 五大分区指的就是虚拟内存。
  8. 虚拟内存空间为4G
  9. 虚拟内存说白了就是一张表&#xff0c;一张映射物理内存的表。
    PageFault
    CPU访问数据时&#xff0c;发现虚拟内存中的数据没有加载到物理内存中时&#xff0c;会执行缺页中断&#xff1a;缺页异常(pagefault)&#xff0c;将虚拟内存中的数据加载到物理内存中&#xff0c;然后再访问物理内存。这个过程是消耗时间的&#xff0c;但消耗的时间是毫秒级别&#xff0c;用户基本感知不到。但是在应用程序启动阶段&#xff0c;会同时出现大量的PageFault&#xff0c;因为启动时加载的代码远远不止一页&#xff0c;有可能几百页甚至上千页。
    我们知道一页有16K&#xff0c;但是一页中可能只访问了一个方法。而在启动时&#xff0c;可能所需要的数据的总量才16K&#xff0c;即一页大小&#xff0c;但是他却被分配到了1000个页中&#xff0c;因此为了那16K的数据&#xff0c;加载16 * 1000页大小的数据。
    物理内存不够用
    当物理内不够用时&#xff0c;操作系统会直接将某一块不活跃的内存覆盖掉。进行加载。
    ASLR
    早期的计算机是用物理内存直接访问的。但应用加载到内存中某个位置是无法确定的&#xff0c;那么每次加载时就需要重新定位&#xff08;重定向&#xff09;
    而现在的计算机是用虚拟内存访问的&#xff0c;虚拟内存都是从0开始访问的&#xff0c;这个就出现了个安全隐患的问题。即可以定位到某个地址进行直接访问&#xff0c;由此引发ASLR技术。

加载顺序

自定义代码&#xff0c;如下&#xff0c;查看他的加载顺序

void test1() {printf("1");
}void test2() {printf("1");
}- (void)viewDidLoad {[super viewDidLoad];printf("viewDidLoad");test1();
}&#43; (void)load {printf("load");test2();
}

我们代码的实现会变成二进制文件&#xff0c;那么代码的排序顺序呢&#xff1f;
修改Write Link Map File选项为YES
在这里插入图片描述

编译之后找到当前项目对应的Intermediates.noindex文件夹。找到TestDemo-LinkMap-normal-arm64.txt文件
在这里插入图片描述

查看该文件内容
在这里插入图片描述


  • Address 地址
  • Size 大小
  • File 文件
  • Name 名字

在链接时首先是按照文件顺序&#xff0c;即编译的顺序Conmpile Sources&#xff0c;然后在按照函数的书写顺序进行排序。
在这里插入图片描述

这种方式就造成了在启动时刻的代码分别分布在了不同的文件里面&#xff0c;以及页中。
优化思路
将所有启动时刻需要调用的方法&#xff0c;排列在一起&#xff0c;这就是二进制重排


查看页面中断的次数

使用Instruments软件中的System Trace功能进行查看。在测试时&#xff0c;你的应用一定要进行冷启动。
在这里插入图片描述

我这里并没有真正的冷启动&#xff0c;大家可以多测几次&#xff0c;这主要是物理内存中并没有删除&#xff0c;而是在再次启动时&#xff0c;直接使用了物理内存中的内容。


二进制重排初体验

ld提供了order_file文件&#xff0c;用于二进制重排。
objc源码中我们可以看到libobjc.order文件&#xff0c;这就是order_file文件
在这里插入图片描述

打开这个文件&#xff0c;进行查看
在这里插入图片描述

这里都是一些二进制名称&#xff0c;因此苹果的objc也进行了二进制重排。
使用MachOView软件打开编译完成的可执行文件。
在这里插入图片描述

其中TEXT为代码文件&#xff0c;DATA为数据文件。那么通过link map文件可以查找到函数对应的位置&#xff0c;以及该函数的汇编实现。
在工程的根目录下创建test.order文件
内容如下

_main
-[ViewController viewDidLoad]
-[Controller hello]
_test1

其中-[Controller hello]方法不存在&#xff0c;在工程中配置Order file文件。如下
在这里插入图片描述

运行或者Build一下&#xff0c;重新查看link map文件
在这里插入图片描述

有文件内容可知&#xff0c;不仅进行了重排&#xff0c;而且还进行了错误去除。
问题
如果做这样一件事&#xff0c;需要写一个完整的order_file出来。那么所有的方法名称怎么获取&#xff1f;
思路


  1. fishhook 可以hook系统函数
    1. 如果我们hook住objc_msgSend函数是否可行&#xff1f;
    2. 由于objc_msgSend函数是可变参数&#xff0c;因此hook的部分需要写汇编
    3. 但是BlockSwiftC函数并不是使用objc_msgSend函数调用的
  2. Clang插桩&#xff1a;100%符号的覆盖&#xff0c;因为Clang解析时会将文件的进行词法分析与语法分析。

—持续更新中&#xff01;—


推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 第四讲ApacheLAMP服务器基本配置Apache的编译安装从Apache的官方网站下载源码包:http:httpd.apache.orgdownload.cgi今 ... [详细]
  • IDApro反编译exe时生成的C文件中#include的defs.h文件在IDA目录下plugins文件夹内*Thisfilecontainsdefinition ... [详细]
  • 逆向工具之unidbg(在pc端模拟执行so文件中的函数)
      昨天在逆向某App的时候,发现有个加密工具类中的native方法是用C语言编写的,隐藏在so文件中。某大佬推荐逆向工具unidbg,能在pc端直接调用so文件中的函数,最终成功 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • oracle安装时找不到启动,Oracle没有开机自启是怎么回事?这一步骤很重要
    重启Oracle数据库重启Oracle数据库包括启动Oracle数据库服务进程和启动Oracle数据库两步,大家继续往下看。按照《【Oracle】什么?作为DBA&# ... [详细]
  • 目录Atlas介绍Atlas部署Atlas基本管理Atlas结合MHA故障恢复读写分离建议Atlas介绍Atlas是由Qihoo360Web平台部基础架构团队开发维护的一个基于My ... [详细]
  • 【基础部分】之SMTP相关配置
    SMTP一、准备工作修改两个主机的主机名1.mailqq.qq.com2.mail163.163.com先配置dns邮件域名在mailqq.qq.com主机上配置dns配置etcn ... [详细]
  • php和jq开发怎么使用es6,PHP与jquery
    本文目录一览:1、phpstorm怎么使用es6语法 ... [详细]
  • 认识Cutestrap,一个轻量级CSS框架
    CutestrapisabrandnewCSSframework.ThisarticlepresentsCutestrap’sfeaturesandputstheframework ... [详细]
  • 嗨,我想用多处理来加速我的代码。但是,apply_async对我不起作用。我试着做一个简单的例子,比如:frommultip ... [详细]
  • 为什么java打不开jsp_解决SpringBoot启动过后不能访问jsp页面的问题(超详细)
    1、首先看SSM(Spring+SpringBoot+Mybatis)的依赖xsi:schemaLocation=http://maven.apache.org/PO ... [详细]
author-avatar
滒娶伱
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有