Keke Yezi

Better Late Than Never

我是叶斌 (Kekeyezi) 就职国泰君安.


Coding & Game & 初级奶爸

iOS App Optimization series 3 内存/CPU优化

生在ARC时代我们是幸运的,还记得初期学习OC 就是在iOS5,那个时候还是MRC时代,又是初学,分不清内存怎么管理,各种崩溃各种酸爽。后面引入ARC机制后,对开发者总算友好了很多,但是不注意还是会引起内存问题。实际情况下解决稳定增长的内存问题并不难(毕竟能够复现),难就难在开发并不知道自己开发的功能发生了内存问题,那么我们就需要借助一些工具提醒我们项目里的内存问题。

下面我们从3个优化方向来总结一下 (配合自制demo食用更佳)

memory

减少内存泄露

定位问题

1.引起 内存泄露的点

  • 循环引用
  • nstimer
  • 大图延迟释放
  • ….

2.查看哪里没有dealloc,怎么定位出问题的vc或者类

解决问题

循环引用 目前我觉得最好的方案就是 FB的FBMemoryProfiler,轻量级的就是腾讯之前分享的MLeaksFinder。

关于图片内存 imageName 和 imageWithContentOfFile了解一下.

降低内存使用峰值

记录内存峰值。Xcode的那个毕竟不能release上用,生产用户无法验证。APM里面有一块也是关于记录内存情况的。我们可以抽样记录内存情况然后分析。

在一些使用场景里,比如整个页面初始化,要分配整个使用内存,批量的图片处理(或者变量),会出现段时间需要加载大量内容,占用过高的内存.需要利用autorelasepool 进行优化

怎么测量内存数值?这是网上找到和Xcode显示内存最接近的函数了。

// 获取当前应用的内存占用情况,和Xcode数值相近
+ (double)getMemoryUsage {
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    if(task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count) == KERN_SUCCESS) {
        return (double)vmInfo.phys_footprint / (1024 * 1024);
    } else {
        return -1.0;
    }
}

过多线程开辟 也会消耗大量内存

减少内存异常引用

crash BAD_EXCESS

这个问题比较复杂,线上的bug 除了exception 就是这个引起的崩溃最多了,也有比较多的优化空间。

搞一个自动增加内存的工具,模拟app高内存 高cpu状态下的情况。模拟线上环境

静态分析代码也能辅助预防一些问题。

CPU

CPU最可能影响的简单的主要是布局和IO操作。

当然也有一些特殊的工具类App 比如音视频或者地图 因为使用的东西特殊可能会带来一定的CPU消耗。这块因为接触的项目少并没有太多了解,但是觉得定位出问题知道哪个模块有问题再对症下药解决的希望就比较高..

最接近Xcode 自带CPU的数据方法如下:

+ (double)getCpuUsage {
    kern_return_t           kr;
    thread_array_t          threadList;         // 保存当前Mach task的线程列表
    mach_msg_type_number_t  threadCount;        // 保存当前Mach task的线程个数
    thread_info_data_t      threadInfo;         // 保存单个线程的信息列表
    mach_msg_type_number_t  threadInfoCount;    // 保存当前线程的信息列表大小
    thread_basic_info_t     threadBasicInfo;    // 线程的基本信息
    
    // 通过“task_threads”API调用获取指定 task 的线程列表
    //  mach_task_self_,表示获取当前的 Mach task
    kr = task_threads(mach_task_self(), &threadList, &threadCount);
    if (kr != KERN_SUCCESS) {
        return -1;
    }
    double cpuUsage = 0;
    for (int i = 0; i < threadCount; i++) {
        threadInfoCount = THREAD_INFO_MAX;
        // 通过“thread_info”API调用来查询指定线程的信息
        //  flavor参数传的是THREAD_BASIC_INFO,使用这个类型会返回线程的基本信息,
        //  定义在 thread_basic_info_t 结构体,包含了用户和系统的运行时间、运行状态和调度优先级等
        kr = thread_info(threadList[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount);
        if (kr != KERN_SUCCESS) {
            return -1;
        }
        
        threadBasicInfo = (thread_basic_info_t)threadInfo;
        if (!(threadBasicInfo->flags & TH_FLAGS_IDLE)) {
            cpuUsage += threadBasicInfo->cpu_usage;
        }
    }
    
    // 回收内存,防止内存泄漏
    vm_deallocate(mach_task_self(), (vm_offset_t)threadList, threadCount * sizeof(thread_t));
    
    return cpuUsage / (double)TH_USAGE_SCALE * 100.0;
}

写在最后:

Better Late Than Never ,愿我们都能跨出自己的第一步。

站在巨人的肩膀上,感谢前辈们的文章。

参考链接:

计算内存问题

http://www.samirchen.com/ios-app-memory-usage/

总结

https://github.com/SilongLi/AppPerformance

instrument 方法

https://blog.csdn.net/clovejq/article/details/78689759

微信读书的库

https://wereadteam.github.io/2016/02/22/MLeaksFinder/

https://juejin.im/entry/5965f8856fb9a06bad654502

内存泄露场景

https://www.jianshu.com/p/e9d989c12ff8

http://www.olinone.com/?p=25

野指针内存总结

http://www.cocoachina.com/ios/20180917/24893.html

自动管理 deallock 释放通知?

https://cocoapods.org/pods/CYLDeallocBlockExecutor

库的总结

http://cnsue.me/2017/04/03/iOS%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E8%87%AA%E5%8A%A8%E6%A3%80%E6%B5%8B/

自写leakfinder

http://yanfeng.life/2017/11/23/YFMemoryLeakDetector-intro/

市面上开源的方案

http://cnsue.me/2017/04/03/iOS%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E8%87%AA%E5%8A%A8%E6%A3%80%E6%B5%8B/

https://github.com/facebook/FBMemoryProfiler

http://wereadteam.github.io/2016/07/20/MLeaksFinder2/

https://code.facebook.com/posts/583946315094347/automatic-memory-leak-detection-on-ios/

instrument教学

http://www.cocoachina.com/ios/20161013/17759.html

介绍野指针

http://www.cocoachina.com/ios/20180917/24893.html

最近的文章

iOS 代码质量随想

借着今天公司有分享设计模式 简单记录下自己对“高”质量代码和设计模式的理解。众所周知设计模式有六大核心的思想原则。引用一段百度百科的介绍 传送门为什么要提倡“Design Pattern呢?根本原因是为了代码复用,增加可维护性。那么怎么才能实现代码复用呢?面向对象有几个原则:单一职责原则 (Single Responsiblity Principle SRP)开闭原则(Open Closed Principle,OCP)、里氏代换原则(Liskov Substitution Princip...…

代码质量继续阅读
更早的文章

iOS RunLoop

前言:​ 2019年的第一篇博客 。如果说每年的都有一个主题,那么我对自己2019年的要求就是夯实基础。之前很多概念要么很早就了解过,要么知识已经更新迭代。所以导致很多知识不用已经遗忘或者就是当时根本也理解的不够透彻。借着培养写博客的习惯梳理一遍自己的知识点,夯实一下自己的基础。​ 其实RunLoop的知识也不是新鲜知识,很多很好的博客从2015年就从源码层次去分析过了,自己之前对RunLoop的理解也是一些很浅的概念。这次就不从原理讲解了,从文末的资料就可以看到大部分很高质量的博客。我认...…

iOS继续阅读