1 启动优化
在iPhone的启动方式中,分为冷启动和热启动两种方式:
- 1、冷启动(Cold Launch):从零开始启动APP ,需要系统新创建一个进程进行启动,这是一次完整的启动;
- 2、热启动(Hot Launch):APP已经在内存中,在后台存活着,再次点击直接打开
我们一般说的启动优化是指: 冷启动时的优化;
要达到优化启动的目的,就需要知道在APP启动的时候,有哪些流程,做了哪些事情;
APP的启动过程是从:点击应用icon图标,到启动图展示,再到首页展示完成的过程:
打印查看启动时间:
通过添加环境变量可以打印出APP的启动时间分析(Edit scheme -> Run -> Arguments)
1、将DYLD_PRINT_STATISTICS设置为1
打印如下:
2、 如果需要更详细的信息,那就将DYLD_PRINT_STATISTICS_DETAILS设置为1
打印如下:
1.1 Main()函数执行前
dyld(dynamic link editor):动态链接器,用来装载Mach-O文件(可执行文件、动态库等)
-
dyld 加载可执行文件mach-o文件到内存中
- 装载APP的可执行文件,同时会递归加载所有依赖的动态库
- 当dyld把可执行文件、动态库都装载完毕后,会通知Runtime进行下一步的处理
-
runtime加载类和分类:
- 调用map_images进行可执行文件内容的解析和处理
- 在load_images中调用call_load_methods,调用所有Class和Category的+load方法
- 进行各种objc结构的初始化(注册Objc类 、初始化类对象等等)
- 调用C++静态初始化器和attribute((constructor))修饰的函数
所有初始化工作完成以后,dyld会调用main函数,接下里就是UIApplicationMain函数的流程
1.1.2 启动优化:
-
1、dyld
- 减少动态库、合并一些动态库(定期清理不必要的动态库)
- 减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类)
- 减少C++虚函数数量
- Swift尽量使用struct
-
2、runtime
- 用+initialize方法和dispatch_once取代所有的
- attribute((constructor))、C++静态构造器、ObjC的+load
-
3、main
- 在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在finishLaunching方法中
- 按需加载
2. 安装包ipa瘦身
官方 App Thinning
App Thinning 是由苹果公司推出的一项可以改善 App 下载进程的新技术,主要是为了解决用户
下载 App 耗费过高流量的问题,同时还可以节省用户 iOS 设备的存储空间。
现在的 iOS 设备屏幕尺寸、分辨率越来越多样化,这样也就需要更多资源来匹配不同的尺寸和分 辨率。 同时,App 也会有 32 位、64 位不同芯片架构的优化版本。如果这些都在一个包里,那 么用户下载包的大小势必就会变大。
App Thinning 会专门针对不同的设备来选择只适用于当前设备的内容以供下载。比如:iPhone 6 只会下载 2x 分辨率的图片资源,iPhone 6plus 则只会下载 3x 分辨率的图片资源。
2.1、使用Asset
- 使用Asset,将图片区分,对应的2X图片和3X图片,在上传appStore以后会做不同的变体,减少下载包的大小
例如我现在的项目,由于是老项目,所有的图片都是直接放在工程中的,所以打包的时候没有区分设备,都会直接打包的app包中;
如果使用了现在的Asset,将2x图和3x图区分开来,在appstore下载App的时候,会根据设备,只下载对应的图片,如果使用的是2x设备则只下载2x的图片,3x的设备只下载3x的图片;
这样就会减小从appstore下载时App包的大小。
2.2、删除无用的图片资源
项目中如果迭代过很多版本,图片资源就会很多,需求在更新的同时,所以可能会有很多无用的图片;
所以要使用一定的方法找到项目中不用的图片,并剔除掉;
- 通过 find 命令获取 App 安装包中的所有资源文件,比如 find /Users/daiming/Project/ - name。
- 设置用到的资源的类型,比如 jpg、gif、png、webp。
- 使用正则匹配在源码中找出使用到的资源名,比如 pattern = @"@"(.+?)""。
- 使用 find 命令找到的所有资源文件,再去掉代码中使用到的资源文件,剩下的就是无用资源
了。 - 对于按照规则设置的资源名,我们需要在匹配使用资源的正则表达式里添加相应的规则,比如
@“image_%d”。 - 确认无用资源后,就可以对这些无用资源执行删除操作了。这个删除操作,你可以使用
NSFileManger 系统类提供的功能来完成。
- 使用第三方软件:LSUnusedResources 链接
下载完成以后直接运行,然后选择工程所在的目录,直接search,如果项目比较大的话,search的时间也会比较久;
运行以后,直接会显示无用的图片,位置和大小:
这里选出来的图片需要斟酌一下再去工程里面删除,毕竟也是用正则表达式筛选出来的,有可能会有误差,如果检查过确实没用,就可以直接删除。
2.3、图片资源压缩
删除了无用的图片资源,有用的图片资源其实也是可以优化的,那做法就是将图片进行压缩和解压处理:
对于 App 来说,图片资源总会在安装包里占个大头儿。对它们最好的处理,就是在不损失图片 质量的前提下尽可能地作压缩。目前比较好的压缩方案是,将图片转成 WebP。WebP 是 Google 公司的一个开源项目。
- 将图片转化成WebP,由于将图片转化成WebP和解压的过程消耗较大,所以可以将图片大于100k的进行转化,其他的小图片不做处理
2.4、删除无用代码
App 的安装包主要是由资源和可执行文件组成的,所以我们在掌握了对图片资源的处理方式后, 需要再一起来看看对可执行文件的瘦身方法。
可执行文件就是 Mach-O 文件,其大小是由代码量决定的。通常情况下,对可执行文件进行瘦 身,就是找到并删除无用代码的过程。而查找无用代码时,我们可以按照找无用图片的思路, 即:
- 找出方法和类的全集;
- 找到使用过的方法和类;
- 接下来,取二者的差集得到无用代码;
- 最后,由人工确认无用代码可删除后,进行删除即可。
2.4.1、使用Link Map 统计所有的方法和类
在build settings中搜索link map,然后设为YES;
设置build的输出路径,指定一个文件夹,然后指定输出文件的格式和名字,一般是txt格式,设置好路径后,直接运行,就会在指定的文件夹下生成一个.txt文件;
打开txt文件,以.o结尾的,都是生成的目标文件,如图:
测试demo我创建了两个类,一个YYPerson,一个YYStudent;
LinkMap 文件分为三部分:Object File、Section 和 Symbols。如下图所示:
- Object File 包含了代码工程的所有文件;
- Section 描述了代码段在生成的 Mach-O 里的偏移位置和大小;
- Symbols 会列出每个方法、类、 block,以及它们的大小。
通过 LinkMap ,你不光可以统计出所有的方法和类,还能够清晰地看到代码所占包大小的具体 分布,进而有针对性地进行代码优化。
如果项目较为复杂,分析起来可能会很困难;
可借助第三方工具解析LinkMap文件:链接
运行以后直接打开txt文件:
这个软件能够清晰的看到有哪些类,以及每个类的大小;
2.4.2、使用 MachOView 统计所有使用过的类
在products中找到.app包,在finder中显示,然后右击显示包内容,找到可执行文件:
然后使用 MachOView打开可执行文件:
如图上所示,我们可以看到 objc__selrefs 和 objc__classrefs 以及objc__superrefs 这三个 section。
其实这3个里面都是通过消息机制,objc__msgSend()函数调用过的对象方法,类方法,类,元类等;
但是,这种查看方法并不是完美的,还会有些问题。原因在于, Objective-C 是门动态语言,方法调用可以写成在运行时动态调用,这样就无法收集全所有调用的方法和类。所以,我们通过这 种方法找出的无用方法和类就只能作为参考,还需要二次确认。
2.5 编译器优化
Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default设置为YES
去掉异常支持,Enable C++ Exceptions、Enable Objective-C Exceptions设置为NO, Other C Flags添加-fno-exceptions
利用AppCode检测未使用的代码:菜单栏 -> Code -> Inspect Code
其实启动优化和ipa瘦身,在于平时的很多细节,比如如果是不用的图片,要立即删除,不用的类和方法,也要及时清理,我们一般不删除的原因是怕以后还有可能用到,但是一般都有版本控制,删除了以后再要用可以从之前的版本中去找;