摆脱Timer内存泄露

作者: 忧郁的小码仔 | 来源:发表于2016-08-18 11:09 被阅读440次

大家应该或多或少的让Timer给整过,尤其是一个控制器里面有N多个Timer的时候,时不时的会给你带来内存上的各种问题。下面,来用block来解决这个问题。


首先,大部分情况下,我们是这样使用Timer的:

    weak var myTimer: Timer!
    override func viewDidLoad() {
        super.viewDidLoad()
        myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerAction(timer:)), userInfo: nil, repeats: true)
    }
    
    func timerAction(timer: Timer) {
        print("timer action")
    }
    
    deinit {
        print("viewcontroller deinit")
    }

这种情况下,一定要在deinit调用之前手动调用 myTimer.invalidate() 才能将Timer和控制器释放掉,不然就会造成循环引用。(有人可能会问: Timer我不是用了weak了吗?为什么还会造成循环引用?)。

关于这个问题,首先要搞清楚,既然你用了weak,而timer仍然可以使用,说明了什么问题?说明了还有强引用在指向timer,才使得它没有被销毁。这个强引用就来自runloop。说到runloop,可以扯出一大堆的知识点(runloop相关文章),这里大家可以参考Timer的官方解释。 也就是说runloop会强引用timer,所以控制器里面只需要weak来定义就可以了。同时,timer又用target强引用了控制器。

所以,如果你没有调用timer的invalidate(),timer就会一直存在,对控制器的强引用也会一直存在。

那么,怎么来解决这个问题呢?

直接上代码:

// 这里不存在循环引用问题,所以可以不使用weak,当然,你也可以使用。 
var myTimer: Timer! 
override func viewDidLoad() {
        super.viewDidLoad()
// block中使用self,一定要声明[weak self],不然会造成循环引用
        myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, repeats: true, action: { [weak self] in 
            print("time action")
            print(self?.count)
        })
    }
    
    deinit {
        print("viewcontroller deinit")
    }



extension Timer {
    private class TempWrapper {
        var timerAction: () -> ()
        weak var target: AnyObject?
        
        init(timerAction: () -> (), target: AnyObject) {
            self.timerAction = timerAction
            self.target = target
        }
    }
    
    public class func scheduledTimer(timeInterval: TimeInterval, target: AnyObject, repeats: Bool = false, action: () -> ()) -> Timer {
        
        return scheduledTimer(
            timeInterval: timeInterval,
            target: self,
            selector: #selector(self._timeAction(timer:)),
// 这里不能直接传action参数,因为() -> () 是不能赋值给AnyObject类型的(同Objective-C
//中的id类似,代表任意class类型),所以,这里使用TempWrapper类来包装一下
            userInfo: TempWrapper(timerAction:action, target: target), 
            repeats: repeats
        )
    }
    
    class func _timeAction(timer: Timer) {
        if let tempWrapper = timer.userInfo as? TempWrapper {
            if let _ = tempWrapper.target {
                tempWrapper.timerAction() 
            } else {
// 如果控制器已经销毁,就把timer invalidate掉
                timer.invalidate() 
            }
        }
    }
}

这里,给Timer做了一个扩展,自己来定义一个方法来包装Timer原来的方法,目的就是摆脱原来Timer方法中target对控制器的强引用。(新方法中的target参数只是用来判断控制器是否已经销毁,而且是weak,不会造成强引用)

测试demo

相关文章

  • 摆脱Timer内存泄露

    大家应该或多或少的让Timer给整过,尤其是一个控制器里面有N多个Timer的时候,时不时的会给你带来内存上的各种...

  • 项目中优化细节

    一.内存优化 1.减少内存泄露。如Timer,Delegate,Block,CoreFoundation对象(C对...

  • 我是如何实现自释放timer的

    引言 我们都知道timer在使用的时候有很多坑,比如强引用target导致循环引用,甚至内存泄露问的,timer触...

  • 内存泄露系列文章(一) - 内存泄露原因及影响

    前言 内存泄露系列文章内存泄露系列文章(一) - 内存泄露原因及影响内存泄露系列文章(二) - 内存泄露监测及分析...

  • 内存泄露系列文章(三) - 内存泄露解决方案

    前言 内存泄露系列文章内存泄露系列文章(一) - 内存泄露原因及影响内存泄露系列文章(二) - 内存泄露监测及分析...

  • 内存溢出与内存泄露

    目录 [TOC] 1 内存泄露与内存溢出的区别 1.1 内存泄露 内存泄露(Memory Leak),指的是堆内存...

  • 内存泄漏详细分析

    首先,什么是内存泄露?经常听人谈起内存泄露,但要问什么是内存泄露,没几个说得清楚。内存泄露是指无用对象(不再使用的...

  • Java弱引用学习 WeakHashMap、ReferenceQ

    上一篇文章 Java内存泄露学习 ThreadLocal真的会内存泄露吗 提到ThreadLocal内存泄露的问题...

  • 内存优化

    内存优化主要是分析内存泄露和内存溢出。将从内存是怎么分配,内存怎么出现泄露和溢出,用工具判断什么情况出现泄露,找出...

  • 内存优化

    内存泄露的原因 四种引用类型 常见的内存泄露 1.内部类导致内存泄露 Handler 2.Context导致内存泄...

网友评论

    本文标题:摆脱Timer内存泄露

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