iOS性能优化 -内存泄露!

2,823 阅读4分钟

这篇文章是基于本公司项目记录的一些内容,也写了很久了,一直没发

问题

常见内存泄露原因:

block循环引用

强引用delegate

NSTimer使用不当

通知或监听未移除

典型例子 - block循环引用

//1:属性:内存策略-强引用,我们一般的全局UIView都是强引用的
@property (strong, nonatomic) LGPlusButtonsView *plusButtonsViewMain;

//2:加载
    _plusButtonsViewMain = [LGPlusButtonsView plusButtonsViewWithNumberOfButtons:3
                                                         firstButtonIsPlusButton:YES
                                                                   showAfterInit:YES
                                                                   actionHandler:^(LGPlusButtonsView *plusButtonView, NSString *title, NSString *description, NSUInteger index)
                            {
                                if (1 == index) {
                                    [_plusButtonsViewMain hideButtonsAnimated:YES completionHandler:^{
                                        //action1:此时vc有plusButtonsViewMain的强引用,而plusButtonsViewMain又使用self。典型的block循环引用
                                        [self xxx];
                                    }];
                                } else if (2 == index) {
                                    [_plusButtonsViewMain hideButtonsAnimated:YES
                                                            completionHandler:^{
                                                                //action2
                                                                [self xxx];
                                                            }];
                                }
                            }];

[self.rootView addSubview: _plusButtonsViewMain];

典型例子 - 强引用delegate

典型例子 - NSTimer使用不当

典型例子 - 通知或监听未移除

demo分析

app运行起来,稍等片刻,待内存稳定后,继续分析下一波操作:

这个运行内存具体跟设备也有关系,就事论事,先看下初始内存大小:42.3m

接着连续操作一波没有内存泄露的页面功能:比如云资源模块

看上面内存图:

第一个红框标记的,是连续的进入退出云资源模块,看内存山峰型的上爬,存在两方面可能:一是由于快速的进入退出,网络资源没有加载完。二是快速退出,内存没来得及释放,arc系统的自动释放内存机制也是有策略的,不会一退出立马回收。

第二个红框标记的,是操作慢下来后,资源能得到充分的加载,展示的图片也都缓存完了(针对imageNamed加载的图片),看每次进入都会有个ui渲染的内存高峰,退出后,内存恢复稳定值。

上面是没有内存泄露的,下面来看有内存泄露的内存耗用图:

这波操作不是快速的进入退出,是资源充分缓存加载后退出,再进入,看图,峰谷不是那么的陡峭,有个平缓期,能说明不是快进快出。但是内存并没有恢复到稳定值,几波操作后,内存一直在爬升,最后停止操作,开始了内存稳定期。能说明啥?

1:模块进入,网络请求,资源加载,ui渲染等,内存回到峰值

2:退出,能释放的释放了,内存泄露处及以后资源释放流程走不到了,内存有所下降,但没有恢复到稳定值。那些没有释放的,再次进入也不会被管理

3:多次的重复上面1、2。内存就一直攀升

优化内存泄露

一般的block循环引用,xcode自动提醒了:xxx,大家都遇到过

但是有些xcode静态识别不了,一般使用product-analyze,和product-profile-leaks。具体的instruments中的leaks检查,网上资料很多,这里不赘述了,提点需要注意的:

call tree,就是内存泄露代码的调用栈:是自下而上的,最上面是栈顶。。。????????????

product-analyze

这个系统工具,一分析,一大堆内存泄露,有以下这么几类:

  • Dead store
  • Logic error
  • Core Foundation/Objective - c
  • Memory(Core Foundation/Objective - c)
  • Memory error
  • API Misuse(Apple)

一般Dead store中最多,这类是代码不严谨的问题:unused的变量,无用的initialization,不合法的返回值等。检查这类问题,xcode还有个强大的工具:Debug Memory Graph ???????????????????

真正的问题在Memory(Core Foundation/Objective - c)和Memory error分类里,点击内存泄露图标,可以查看内存泄漏的流程逻辑

个人觉得开发过的代码,有必要analyze一下,一是提高代码质量,二是提高自己编码严谨能力

工具

通过上面一系列操作,然并卵,一些导致对象无法释放的block循环引用或强引用代理并不能体现出来,你的vc退出时候依然没有走到dealloc,抓狂不?所以,还需要更深入的分析:

MLeaksFinder

FBRetainCycleDetector

FBRetainCycleDetector是FaceBook的产物,专门用于循环检测的,这个也不赘述了,MLeaksFinder 1.0内集成的有FBRetainCycleDetector,据说1.1去掉了,这里不多说。

注意: 个人用的xcode10,集成MLeaksFinder后,编译报错:

Multiple commands produce '/Users/xxt/Library/Developer/Xcode/DerivedData/Zxjx-dpyxnqyuorobwacwcqektxdxptrt/Build/Products/Debug-iphoneos/Hnxxt.app':
1) Target 'Hnxxt' has create directory command with output '/Users/xxt/Library/Developer/Xcode/DerivedData/Zxjx-dpyxnqyuorobwacwcqektxdxptrt/Build/Products/Debug-iphoneos/Hnxxt.app'
2) That command depends on command in Target 'Hnxxt': script phase “[CP] Copy Pods Resources”

这是xcode10的原因,不赘述,File - Workspace Settings - Shared Workspace Settings:Build System 改为:Legacy Build System

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发公众号:编程大鑫,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(无偿提供一些群主收藏的免费学习书籍进阶资料!)

以上自行完成后,可以逐个页面去检查内存泄露了:

以上面的典型例子为例,检查结果像下面这样:

说明你这个页面有内存泄露,没法释放