What Is Difference Between Self.Timer = Nil VS [Self.Timer Invalidate] in iOS

Why does timer continue to execute after invalidation?

The scheduledTimer(withTimeInterval:repeats:block:) method:

After interval seconds have elapsed, the timer fires, executing block.

The invalidate() method:

Stops the timer from ever firing again

You are correct in your discovery that invalidating a timer will not interrupt a currently executing block, but will only prevent future executions of that block.

Do I need to invalidate/release the timer?

This kind of timer is retained only by the run loop. Since you don't have a pointer to it, you can't invalidate it. It also is going to retain its target (self), so it's not possible for self to deallocate prior to the timer firing. After it fires, the run loop will release it, so it will deallocate, causing it to release self, and allowing that object to deallocate (assuming there are no other retains on it).

In most case I do not recommend ignoring the return value from scheduledTimerWithTimeInterval:… because it makes it impossible for you to invalidate the timer. But if that behavior is exactly what you want (sometimes in global timers created in AppDelegate), then it will work fine with no additional code.

swift invalidate timer doesn't work

The usual way to start and stop a timer safely is

var timer : Timer?

func startTimer()
{
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
}
}

func stopTimer()
{
timer?.invalidate()
timer = nil
}

startTimer() starts the timer only if it's nil and stopTimer() stops it only if it's not nil.

You have only to take care of stopping the timer before creating/starting a new one.

Difference between weak references in a block and a NSTimer

The whole problem with the selector-based NSTimer technique is that it establishes a strong reference to the object you pass to it. So whether the variable you used to hold the reference to the target you passed to scheduledTimerWithTimeInterval was, itself, strong or weak, is immaterial. Assuming the target reference wasn't nil by the time the selector-based scheduled timer was scheduled, NSTimer will establish its own strong reference. The "weak" vs "strong" nature of the references in calling code only dictates where ARC will place its own memory management calls in the caller's code, but the target is just a simple pointer, and none of this weak vs strong information is conveyed to NSTimer. The selector-based NSTimer will establish its own strong reference that is not resolved until the timer is invalidated.

This is why, when we want to invalidate a timer built via the selector-based method, we have to it in viewDidDisappear or the like, rather than dealloc.

Note, scheduledTimerWithTimeInterval now has a block-based variation for iOS 10 and later, so you can enjoy the weak reference pattern of blocks if you don't have to support earlier iOS versions:

typeof(self) __weak weakSelf = self;
[NSTimer scheduledTimerWithTimeInterval:30 repeats:true block:^(NSTimer * _Nonnull timer) {
// use weakSelf here
}];

Timer is being fired twice and not nilled

Thanks to solenoid and Paulw11 comments, I learned that all executions related to a timer should be done in a serial manner.

Also see invalidate

You must send this message from the thread on which the timer was
installed
. If you send this message from another thread, the input
source associated with the timer may not be removed from its run loop,
which could prevent the thread from exiting properly.

So in addition to invalidating and nilling (you don't want to give non-nil for an object that no longer conveys any meaning), make sure that you:

  1. use the same thread
  2. the thread you are using is serial
  3. depending on where you make changes onto the timer you may or may not need synchronization.

Timer will not Invalidate swift 4

Your problem lies in this part of your code.

Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(timeoutPeriod)), userInfo: nil, repeats: true)

You're creating a Timer but not setting it into the Timer variable you created var timer = Timer()

To fix this you just have to set your Timer variable correctly.

self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(timeoutPeriod)), userInfo: nil, repeats: true)

How to set the timers in [NSTimer] to nil?

It is because you are in a loop, where you cannot mutate the object that is being iterated.

for timer in timers {
// timer is initialized here like "let timer = timers[index]"
}

Invalidate the the timers in the loop, then set

timers = nil

or

timers.removeAll()

NSTimer doesn't stop with invalidate

Read documentation for NSTimer:

There are three ways to create a timer:

  1. Use the scheduledTimerWithTimeInterval:invocation:repeats: or scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: class method to create the timer and schedule it on the current run loop in the default mode.

  2. Use the timerWithTimeInterval:invocation:repeats: or timerWithTimeInterval:target:selector:userInfo:repeats: class method to create the timer object without scheduling it on a run loop. (After creating it, you must add the timer to a run loop manually by calling the addTimer:forMode: method of the corresponding NSRunLoop object.)

  3. Allocate the timer and initialize it using the initWithFireDate:interval:target:selector:userInfo:repeats: method. (After creating it, you must add the timer to a run loop manually by calling the addTimer:forMode: method of the corresponding NSRunLoop object.)

You are using method which already adds it to mainLoop from 1. - you need to remove this line or create a timer with 2. approach and leave manual adding.

Also remember that you must send invalidate message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

Deinit / Invalidate Timer

When you use a scheduled timer with 'target/selector', it retains its target. More specifically, the Runloop retains scheduled timers, in turn which retain their target.

I use this version, which doesn't retain self:

Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] _ in
self?.doSomethingRegularly()
})

And you'll still need to invalidate the above timer in your deinit as well, otherwise you'll leak the timer (but not your class).



Related Topics



Leave a reply



Submit