美文网首页收录OC重点
iOS-性能优化-耗电优化、启动优化、安装包瘦身

iOS-性能优化-耗电优化、启动优化、安装包瘦身

作者: Imkata | 来源:发表于2019-12-30 10:45 被阅读0次

一. 耗电优化

耗电的主要来源

耗电来源.png

CPU处理,Processing
网络,Networking
定位,Location
图像,Graphics

1. CPU、GPU优化

  1. 尽可能降低CPU、GPU功耗
  2. 少用定时器
  3. 优化I/O操作
    ① 尽量不要频繁写入小数据,最好批量一次性写入。
    ② 读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的API,用dispatch_io系统会优化磁盘访问。
    ③ 数据量比较大的,建议使用数据库(比如SQLite、CoreData),因为数据库是由优化过的。

2. 网络优化

  1. 减少、压缩网络数据。
    ① 比如XML文件体积就比较大,所以现在大部分公司都使用json,json体积比较小,现在有些公司也在使用protocol buffer这种格式,有兴趣的可以去研究下。
    ② 上传文件的时候可以压缩文件,服务器拿到文件后再解压。
  2. 如果多次请求的结果是相同的,尽量使用缓存。
  3. 使用断点续传,否则网络不稳定时可能多次传输相同的内容。
  4. 网络不可用时,不要尝试执行网络请求。
  5. 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间。
  6. 批量传输。
    ① 比如下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。
    ② 如果下载广告,一次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载。

3. 定位优化

  1. 如果只是需要快速确定用户位置,最好用CLLocationManager的requestLocation方法。定位完成后,会自动让定位硬件断电。
  2. 如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务。
  3. 尽量降低定位精度,比如尽量不要使用精度最高的kCLLocationAccuracyBest。
  4. 需要后台定位时,尽量设置pausesLocationUpdatesAutomatically为YES,如果用户不太可能移动的时候系统会自动暂停位置更新。
  5. 尽量不要使用startMonitoringSignificantLocationChanges,优先考虑startMonitoringForRegion:。

4. 硬件检测优化

用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测,在不需要检测的场合,应该及时关闭这些硬件。

二. 启动优化

APP的启动可以分为两种:

  1. 冷启动(Cold Launch):从零开始启动APP。
  2. 热启动(Warm Launch):APP已经在内存中,在后台存活着,再次点击图标启动APP。

APP启动时间的优化,主要是针对冷启动进行优化。

通过添加环境变量可以打印出APP的启动时间分析,点击Edit scheme -> Run -> Arguments,给Environment Variables添加一个DYLD_PRINT_STATISTICS,并且把Value设置为1,如下图:

启动打印.png

运行空项目,打印:

Total pre-main time: 646.22 milliseconds (100.0%) //main函数调用之前总耗时
         dylib loading time: 165.83 milliseconds (25.6%) //动态库加载耗时
        rebase/binding time: 385.45 milliseconds (59.6%) 
            ObjC setup time:  61.10 milliseconds (9.4%) //OC结构体准备耗时
           initializer time:  33.64 milliseconds (5.2%) //初始化耗时
           slowest intializers : //比较慢的加载,分别是下面两个动态库
             libSystem.B.dylib :   8.76 milliseconds (1.3%)
    libMainThreadChecker.dylib :  14.72 milliseconds (2.2%)

如果需要更详细的信息,那就将DYLD_PRINT_STATISTICS改为DYLD_PRINT_STATISTICS_DETAILS并且Value也是设置为1,打印如下:

total time: 741.00 milliseconds (100.0%) //总耗时
total images loaded:  258 (0 from dyld shared cache) //镜像加载耗时
total segments mapped: 767, into 102624 pages with 7415 pages pre-fetched //每个块的映射
total images loading time: 296.70 milliseconds (40.0%)
total load time in ObjC:  69.74 milliseconds (9.4%) //加载objc
total debugger pause time: 244.17 milliseconds (32.9%)
total dtrace DOF registration time:   0.13 milliseconds (0.0%)
total rebase fixups:  2,723,656
total rebase fixups time: 300.98 milliseconds (40.6%)
total binding fixups: 286,603
total binding fixups time:  48.64 milliseconds (6.5%)
total weak binding fixups time:   0.44 milliseconds (0.0%)
total redo shared cached bindings time:  61.81 milliseconds (8.3%)
total bindings lazily fixed up: 0 of 0
total time in initializers and ObjC +load:  24.36 milliseconds (3.2%)
libSystem.B.dylib :   2.38 milliseconds (0.3%)
libBacktraceRecording.dylib :   2.95 milliseconds (0.3%)
CoreFoundation :   1.74 milliseconds (0.2%)
Foundation :   1.73 milliseconds (0.2%)
libMainThreadChecker.dylib :  13.70 milliseconds (1.8%)
total symbol trie searches:    133794
total symbol table binary searches:    0
total images defining weak symbols:  22
total images using weak symbols:  62

一般总时间在400ms以内都是比较正常的,如果大于400ms就可以优化了。

1. 冷启动的三大阶段

APP的冷启动可以概括为三大阶段:

  1. dyld 装载阶段
  2. runtime阶段
  3. main阶段

如下图:

冷启动.png

① dyld 装载阶段

  1. dyld(dynamic link editor),它是Apple的动态链接器,可以用来装载Mach-O文件(可执行文件、动态库等)。
  2. 启动APP时,dyld动态链接器会装载APP的可执行文件,同时会递归加载所有依赖的动态库。

什么是可执行文件?
右键点击XX.app选择Show in Finder,找到XX.app,右键点击XX.app选择显示包内容,可以发现里面有个Unix可执行文件,平时我们写的所有代码都在这里面了,这就是一个Mach-O格式的可执行文件。如下图:

包.png

注意:项目没编译的时候上面的XX.app是红色的,并且右键Show in Finder也没东西,编译之后才会多一个XX.app的包。

显示包内容.png 可执行文件.png

但是我们项目开发中也会依赖一些动态库,比如UIKit、Foundation,这些动态库都不是包含在可执行文件里面的,可执行文件里面只有一些依赖信息,比如这个项目依赖哪些动态库,一个动态库也可能依赖另一个动态库。

在dyld阶段,dyld动态链接器会装载可执行文件,以及检查可执行文件依赖哪些动态库,并加载那些动态库,一个动态库也可能依赖另一个动态库,dyld动态链接器就是这样一个一个检查,递归查找,直到装载完所有的动态库到内存中。

当dyld把可执行文件、动态库都装载完毕后,会通知Runtime进行下一步的处理。

② runtime阶段

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

  1. 调用map_images函数进行可执行文件内容的解析和处理
  2. 在load_images函数中调用call_load_methods,调用所有Class和Category的+load方法
  3. 进行各种objc结构的初始化(注册Objc类 、初始化类对象等等)
  4. 调用C++静态初始化器和__attribute__((constructor))修饰的函数

到此为止,可执行文件和动态库中所有的符号(Class,Protocol,Selector,IMP…)都已经按格式成功加载到内存中,被runtime 所管理。

可能你对map_images函数比较熟悉,我们在以前看_objc_init源码的时候接触过这个函数,如下:

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    environ_init();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    //map_images:可执行文件内容的解析和处理
    //load_images函数中调用call_load_methods,调用所有Class和Category的+load方法
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    rwlock_writer_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}
void
load_images(const char *path __unused, const struct mach_header *mh)
{
    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        rwlock_writer_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    //调用所有Class和Category的+load方法
    call_load_methods();
}

③ main阶段

  1. APP的启动由dyld主导,将可执行文件加载到内存,顺便加载所有依赖的动态库,并由runtime负责加载成objc定义的结构。
  2. 所有初始化工作结束后,dyld就会调用main函数,接下来就是UIApplicationMain函数,然后进入AppDelegate的application:didFinishLaunchingWithOptions:方法。

2. 冷启动优化

按照不同的阶段:

  1. dyld 装载阶段
    减少动态库、合并一些动态库(定期清理不必要的动态库)
    减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类)
    减少C++虚函数数量(因为一旦有虚函数就要多维护一张虚表,有虚表的话冷启动的时候就要多耗费一点时间)
    Swift尽量使用struct(因为加载类的时候也耗费时间)

  2. runtime阶段
    用+initialize方法和dispatch_once取代所有的__attribute__((constructor))、C++静态构造器、ObjC的+load方法。

  3. main阶段
    在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在didFinishLaunchingWithOptions方法中,也就是按需加载。

三. 安装包瘦身

上面我们说了,项目中所有的代码、资源都在XX.app包里面,将来Xcode会将这个XX.app包压缩成一个ipa文件,然后上传到AppStore提供给用户下载,如果项目越来越大,那么这个ipa文件就会越来越大。

为了给安装包(IPA)瘦身,我们就要知道安装包有哪些文件组成,安装包(IPA)主要由可执行文件、资源组成

安装包瘦身方式:

① 对于资源(图片、音频、视频等)

  1. 采取无损压缩
  2. 去除没有用到的资源:https://github.com/tinymind/LSUnusedResources

② 对于可执行文件

  1. 编译器优化
    Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default设置为YES(现在的项目已经默认为YES了,一些老项目可能还会为NO)。
  2. 去掉异常支持
    Enable C++ Exceptions、Enable Objective-C Exceptions设置为NO, Other C Flags添加-fno-exceptions。
  3. 利用AppCode(https://www.jetbrains.com/objc/)检测未使用的代码
    菜单栏 -> Code -> Inspect Code。
  4. 编写LLVM插件检测出重复代码、未被调用的代码(这种方式比较高级也比较难)。
  5. 生成LinkMap文件,可以查看可执行文件的具体组成。如下图:
LinkMap.png

如果项目比较大,分析LinkMap文件就会比较麻烦,我们可以借助第三方工具解析LinkMap文件:https://github.com/huanxsd/LinkMap

它其实是个Mac项目,我们下载下来,运行项目 -> 选择文件 -> 点击开始,就能显示每个文件占用多大,我们就能根据文件有目的性的进行优化。如下图:

LinkMap分析.png

相关文章

  • iOS性能优化 - 整理

    本文主要包含: 性能优化 - 卡顿性能优化 - 耗电优化性能优化 - APP启动优化安装包瘦身 一  性能优化 -...

  • iOS底层原理(六):性能优化

    前言 性能优化包括:卡顿检测和优化、耗电优化、启动优化、安装包瘦身几部分组成 一、卡顿检测和优化 1. 屏幕成像原...

  • iOS 性能优化

    iOS的性能优化主要可提现在以前的几个方面:卡顿优化、耗电优化、启动优化、安装包的瘦身。 1、卡顿优化 在了解卡顿...

  • iOS 优化方案

    一、性能优化基本方案 1、卡顿原因以及避免方案2、耗电优化3、启动优化4、安装包瘦身 二、卡顿优化原因以及避免方案...

  • 21.性能优化

    关于iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局优化、电量优化、 安装包瘦身、启动优化、网...

  • iOS App优化:基本工具、业务优化、内存优化、卡顿优化、布局

    关于iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局优化、电量优化、 安装包瘦身、启动优化、网...

  • 性能优化

    关于iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局优化、电量优化、 安装包瘦身、启动优化、网...

  • iOS 性能优化总结

    关于iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局优化、电量优化、 安装包瘦身、启动优化、网...

  • iOS-性能优化-耗电优化、启动优化、安装包瘦身

    一. 耗电优化 耗电的主要来源 CPU处理,Processing网络,Networking定位,Location图...

  • iOS底层原理 - 性能优化 之 耗电优化

    面试题引发的思考: Q: 项目优化从哪几方面着手? 耗电优化、启动优化、卡顿优化、APP瘦身。 Q: 耗电优化的几...

网友评论

    本文标题:iOS-性能优化-耗电优化、启动优化、安装包瘦身

    本文链接:https://www.haomeiwen.com/subject/kepdoctx.html