Timer解除循环引用

代码基于swift 4.0。

一、在不需要的时候invalidate()。

当控制器和timer循环引用时,不能将 timer?.invalidate() 放在 deinit 方法中,因为控制器不会被释放掉,deinit方法不会被调用。

不需要定时器的时候invalidate,例如可以在viewDidDisappear 中释放掉。因为Timer会对target强引用,只有当timer被invalidate才会取消强引用。官方描述:

target
The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated.

    override func viewDidDisappear(_ animated: Bool) {
        timer?.invalidate()
        timer = nil
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureUI()
        
        timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(handerTimer), userInfo: nil, repeats: true)
    }

    @objc func handerTimer() {
        print("timer")
    }

注:尝试传weakSelf Timer.scheduledTimer 的target对象,还是会发生引用循环。因为Timer 对target强引用,强引用一个weak 变量,对变量所指的对象仍然是强引用。

//此种方式无效。
   weak var weakSelf = self
   timer = Timer.scheduledTimer(timeInterval: 2, target: weakSelf!, selector: #selector(handerTimer), userInfo: nil, repeats: true)
二、在timer初始化的block中弱引用控制器。(这种方式仅限于@available(iOS 10.0, *))

timer弱引用控制器,那么就不会造成循环引用,控制器可以正常的释放掉。deinit方法会调用。

    var timer: Timer?
    
    deinit {
        timer?.invalidate()
        timer = nil
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 下面方法@available(iOS 10.0, *)
        timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: { [weak self] (_) in
            self?.handerTimer()
        })
    }

三、第二种方式只能在iOS10.0上使用。为了兼容以下版本,封装一个block类方法,通过userInfo传递。

self -> timer timer --->self timer弱引用self 不存在循环引用
Timer 循环引用自己

    override func viewDidLoad() {
        super.viewDidLoad()
        // [weak self] 不能忘
        timer = Timer.xx_scheduledTimer(withTimeInterval: 2, repeats: true, block: { [weak self] (_) in
            self?.handerTimer()
        })
    }

    @objc func handerTimer() {
        print("timer")
    }

// 写一个Timer的分类。
extension Timer {
    //兼容不同版本
    class func xx_scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void) -> Timer {
        if #available(iOS 10.0, *) {
            return Timer.scheduledTimer(withTimeInterval: interval, repeats: repeats, block: block)
        }
        return scheduledTimer(timeInterval: interval, target: self, selector: #selector(xx_handerTimerAction(_:)), userInfo: Block(block), repeats: true)
    }
    
    // 一定要加class。 Timer是类对象,只能调用类方法,不能调用实例方法(否则报错:[NSTimer xx_handerTimerAction:]: unrecognized selector sent to class 0x1105c30c0')
    @objc class func xx_handerTimerAction(_ sender: Timer) {
        if let block = sender.userInfo as? Block<(Timer) -> Void> {
            block.type(sender)
        }
    }
}

//Block模型类
class Block {
    let type: T
    init(_ type: T) {
        self.type = type
    }
}

你可能感兴趣的:(Timer解除循环引用)