iOS 内存泄漏的发生和问题查找的方向

在我们iOS日常的开发过程中会遇到各种各样的内存泄漏问题,这个问题表面可能看起来并不严重,但实际有很大的弊端,会造成过多内存的使用不会被回收,所有今天我来总结一下我遇到过的内存泄漏的问题以及我查找的思路。

###下面我就来总结一下常见内存泄漏出现的地方。

  1. 在使用Timer造成的内存泄漏

    相信我们在做开发过程会时不时用到Timer,无论你是用作计时器还是倒计时还是延时触发,有时候我们创建了一个Timer并启用了它,在使用完之后往往有一件非常重要的事情会忘记做那就是去把Timer停掉。举个例子。

var mTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(DemoVC.timeLabel), userInfo: nil, repeats: true)
  相信上面的写法大家应该很熟悉吧,启用了一个Timer并获取了Timer的实例,如果这段代码会被重复执行会出现什么情况呢?很明显会出现创建多个Timer的问题,也就是多个Timer同时运行,为什么这么说呢,是因为Timer和当前类大部分是ViewController互相引用根本在重新赋值的时候不会对上一个进行回收,这样就早成内存泄漏了,那么如何做才能避免这个问题呢?我们来总结一下解决方式。
mTimer?.invalidate()
mTimer = nil
其实解决Timer造成的泄漏的方式很简单,就和很久远的时候我们用非arc写代码的时候,需要谁创建谁释放,只要有创建的地方必须对应一个释放的地方就是这个原理,Timer同理。也就是在创建启用Timer后一定会对应一个释放的代码,只要记住在不用Timer或者重新启用新的Timer之前一定要把老的Timer实例用以上的代码释放掉再进行新Timer的创建启用,这样才能保证内存不会泄露,在彻底不用Timer的时候也需要去将其释放掉,比如我们需要离开当前ViewController不再需要它了的时候。
  1. 代理设置造成的内存泄漏

    我现在开发都用swift来写,所以我的所有示例以swift为主。
    在我们开发过程经常会有一些内容需要回调,比如说我们写一个自定义View或者定义了一个ViewModel需要在触发某些数据或操作的时候对ViewController进行回调,那么这些回调我们往往会使用protocol来定义和处理,例如

protocol OnCallProtocol:NSObject {

        func onSuccess()

        func onFail(code:Int)    
}

以上我们定义了一个protocol,下面我们看看在实际中的使用。
比如说我们在我们写的ViewModel和ViewController中使用到了它,在ViewModel中处理某些操作需要对ViewController进行回调。

class ViewModel {
    var mDelegate:OnCallProtocol?
    ...

    fun doSomething() {
        mDelegate?.onSuccess()
    }
}

所以我们需要在ViewController中实现这个代理并传入ViewModel中。

class ViewController : UIViewController, OnCallProtocol{
    var mViewModel:ViewModel = ViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        mViewModel.mDelegate = self
    }

    func onSuccess() {
    }

    func onFailed() {
    }

    override func deinit() {

    }
}

可以通过以上代码看出,我们将ViewController做为代理的实现传入了ViewModel中使用,那么这样正常吗?
最起初我是这么写的,当然我的ViewController中还使用了几个Timer,我发现了我的这个ViewController发生了内存泄漏,也就是deinit方法在关掉ViewController的时候未被调用,于是乎我开始查问题在哪,当然我第一个怀疑的是Timer造成的内存泄漏,然后就是开始使用排除法来查找,当然有时候排除法是最好的方法了,当我把Timer的代码检查过后并没有发现会发生泄漏的地方,于是乎就又把其他一些地方也查找过了包括一些block中weak self的使用的检查,这就头大了没发现问题啊,后来经过同事提醒说
“你查查代理方面”,我擦我这个时候幡然醒悟,我确实用到了一个代理,发现我的代理在引用的地方并么有加一个关键字weak,如果不用弱引用的话会造成互相的循环强引用,我们来修正一下代码如下

class ViewModel {
    weak var mDelegate:OnCallProtocol?
    ...

    fun doSomething() {
        mDelegate?.onSuccess()
    }
}

通过测试发现问题真的被解决了。

目前来说我遇到最多的问题就是这两个,其实总结一下大部分都是循环引用造成的,大家可以朝着循环引用查找内存泄漏的问题。

说点什么

avatar
  Subscribe  
提醒