Kamil Harasimowicz
Kamil Harasimowicz

Reputation: 4984

How to properly remove Timer instance

I am using Timer instance in my project.

I create and add an instance to RunLoop

timer = Timer(timeInterval: 0.1, repeats: true, block: { (timer) in
    print("Hello")
})

if let timer = timer {
    RunLoop.current.add(timer, forMode: .commonModes)
}

Now I am thinking about how to properly stop and remove Timer:

  1. Should I trust in ARC and don't care about it?
  2. Maybe deinit implementation is need. If is it true, what should be inside

    deinit {
        longPressTimer?.invalidate()
        longPressTimer = nil
    }
    

invalidate is nesseccery? = nil ?

I have read serval threads from Stack about it but answers are contradictory. Could someone tell me which way is properly and explain me why?

Upvotes: 0

Views: 3505

Answers (3)

elk_cloner
elk_cloner

Reputation: 2149

First check whether timer exist? if yes then invalidate it like

 if timer!= nil {
    timer.invalidate()
 }

Keep in mind that if you use a timer in any VC invalidate it when you leave that VC(View Controller)

Upvotes: 0

hoang Cap
hoang Cap

Reputation: 804

You need both invalidate() and = nil. Unlike other objects, a timer DOES NOT get deallocated when its retain count reaches zero unless it's stopped (invalidated).

Say, if you have a repeating timer which is currently active. when you set timer = nil, it still persists in memory and keeps triggering the action (probably the iOS has some kinds of mechanism to keep it alive as long as it's still active). So the rule of thumb is: Always call invalidate() and set the timer to nil when you want to get rid of it.

Upvotes: 2

Grzegorz Krukowski
Grzegorz Krukowski

Reputation: 19792

If timer is repeating it won't invalidate as long as it's target is in memory.

The best solution for me was to use this class - which will observe target, and it target is deallocate it will invalidate itself.

final class WeakTimer {
    private weak var timer: Timer?
    private weak var target: AnyObject?
    private let action: (Timer) -> Void

    private init(timeInterval: TimeInterval,
                     target: AnyObject,
                     repeats: Bool,
                     userInfo: Any?,
                     action: @escaping (Timer) -> Void) {
        self.target = target
        self.action = action
        self.timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(fire(timer:)), userInfo: userInfo, repeats: repeats)
        RunLoop.main.add(self.timer!, forMode: .commonModes)
    }

    class func scheduledTimer(timeInterval: TimeInterval,
                              target: AnyObject,
                              userInfo: Any?,
                              repeats: Bool,
                              action: @escaping (Timer) -> Void) -> Timer {
        return WeakTimer(timeInterval: timeInterval,
                         target: target,
                         repeats: repeats,
                         userInfo: userInfo,
                         action: action).timer!
    }

    @objc fileprivate func fire(timer: Timer) {
        if target != nil {
            action(timer)
        } else {
            timer.invalidate()
        }
    }
}

Upvotes: 1

Related Questions