dave_the_dev
dave_the_dev

Reputation: 1635

Swift: test for expiration / invalidation of NSTimer seems to fail

Using Swift, I'm having difficulty testing for the expiration / invalidation of an NSTimer.

In particular, why does it appear that a test like while timer1 != nil { } or while timer1?.valid { } does not escape the null loop after it has been seemingly clearly invalidated and set to nil directly.

I have reviewed several of the questions relating to NSTimer already, and unless there is a version in Obj-C that I simply am not recognizing as the same issue due to my poor comprehension of ObjC properly, I don't believe this is covered (directly).

Please note, I'm not looking to directly "fix" the code, I'm looking for comprehension.

//  ViewController.swift
//  NSTimerTest


import UIKit

class ViewController: UIViewController {

    var z:Int = 0
    var timer1:NSTimer? = nil

    @IBOutlet var lblMessage: UILabel
    @IBOutlet var lbl2: UILabel
    @IBAction func btnPress(sender: AnyObject) {
        doBtnPress()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        z = 0
        lblMessage.text = "Timer was called \(z) times."
        lbl2.text = "waiting for countdown"

    }


    func doBtnPress(){
        lblMessage.text = "doBtnPress!!"
        timer1 = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "doTimedAction", userInfo: nil, repeats: true)

        // All of the below infinitely loop 
        // while timer1?.valid { }  // <=== never escapes, also depreciated in IOS 8.0 ???
        // while timer1 { }       // <==== never escapes
        // while timer1 != nil { } // <==== never escapes 
        // while z >= 0 { }       //  <==== test counter instead, but no use 


        lbl2.text = "timer has been invalidated"
    }

    func doTimedAction(){
        lblMessage.text = "Timer was called \(++z) times."

        if z >= 10 {
            timer1?.invalidate()
            timer1 = nil
            z = -1      
        }

    }     
}

As you can see, I have two labels -- one that is simply updated with the number of times the NSTimer has been invoked, and the other that hold be updated after I invalidate the timer.

With the testing lines commented out as noted above, things work as I expect. The label indicating the number of timer invocations updates each second (or so) and the immediate but factually incorrect display of the statement that "Timer has been invalidated" )

However, it my expectation that de-commenting any of the three lines that follow should allow the timer to run through 10 iterations, and then be invalidated, allowing the "Timer invalidated" message to display. Instead, however, I end up stuck infinitely in the empty loop, as if the condition(s) never come true. The UI does not update at all.

Instead of testing the timer, I also tried testing the counter z, but this does not work either, leading me to believe there is something more elemental at play that I am not understanding. For example, using while z >= 0 { println("z is \(z)")} in the location results in z is 0 -- the reason the loop is infinite. But again, if I comment out the line, z does in fact clearly increment, as the label DOES change, reflecting the cane in the counter z.

Again, I'm looking more to understand WHY things are failing (i.e., what I am failing to comprehend here). I understand, for example, I could make this "work" by having the doTimedAction() func directly update the label --- but that would not help me understand why my tests fail.

Upvotes: 1

Views: 1557

Answers (1)

Grimxn
Grimxn

Reputation: 22487

Whilst you are in the loop, you are blocking the timer from firing, as both the timer and loop are on the same thread.

Check out the documentation on Run Loops, and also this post on creating an NSTimer on a background thread.

Upvotes: 2

Related Questions