Search code examples
iosswiftabstract-classnstimersubclassing

reading public var valid of subclass of NSTimer fails with 'isValid only defined for abstract class'


I have created a subclass of NSTimer which adds (i hope) the pause and resume function to scheduledTimerWithTimeInterval function.

class HCTimer: NSTimer {

    var hcTimerInterval :NSTimeInterval!
    var hcTarget :AnyObject!
    var hcSelector :Selector!
    var hcUserInfo :AnyObject!
    var hcRepeats :Bool!
    var hcTimer :HCTimer!

    class func pausableScheduledTimerWithTimeInterval(ti: NSTimeInterval, target aTarget: AnyObject, selector aSelector: Selector, userInfo: AnyObject?, repeats yesOrNo: Bool) -> HCTimer {

        let hcInstanceTimer = super.scheduledTimerWithTimeInterval(ti,target: aTarget, selector: aSelector, userInfo: userInfo, repeats: yesOrNo) as! HCTimer

        hcInstanceTimer.hcTimerInterval = ti
        hcInstanceTimer.hcTarget = aTarget
        hcInstanceTimer.hcSelector = aSelector
        hcInstanceTimer.hcUserInfo = userInfo!
        hcInstanceTimer.hcRepeats = yesOrNo
        hcInstanceTimer.hcTimer = hcInstanceTimer

        return hcInstanceTimer
    }

    func pause() {
        self.invalidate()
    }

    func resume() {
        self.hcTimer = HCTimer.pausableScheduledTimerWithTimeInterval(hcTimerInterval, target: hcTarget, selector: hcSelector, userInfo: hcUserInfo, repeats: hcRepeats)
    }
}

To use the new class in the existing viewController, i replaced the existing properties of type NSTimer with HCTimer. The timer is started when a button is pressed and the timer is not valid.

class ViewController: UIViewController {

    var severityButtonTimer = HCTimer()

    @IBAction func sortOnSeverityButtonTouchInside(sender: UIButton) {

        // Start animation if none running
        if (!severityButtonTimer.valid) {
            // Change sort order

The code compiles successfully, but when the function the value 'valid' of the HCTimer instance is read it fails with the following exception.

2016-04-08 12:03:25.234 homecaremonitor[45750:17518480] *** Terminating app     due to uncaught exception 'NSInvalidArgumentException', reason: '*** -isValid only defined for abstract class.  Define -[homecaremonitor.HCTimer isValid]!'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010f4c5d85     __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000111269deb objc_exception_throw + 48
    2   CoreFoundation                      0x000000010f4c5cbd +[NSException raise:format:] + 205
    3   Foundation                          0x000000010f9477b9 NSRequestConcreteImplementation + 229
    4   Foundation                          0x000000010f95aa19 -[NSTimer(NSTimer) isValid] + 39
    5   homecaremonitor                     0x000000010ef43f78 

Still trying to fix this, any help is appreciated ! Cheers, Peter

Replaced the subclass HCTimer with HCTimerWrapper as adviced by Wain

class HCTimerWrapper {

    var hcTimerInterval :NSTimeInterval!
    var hcTarget :AnyObject!
    var hcSelector :Selector!
    var hcUserInfo :AnyObject!
    var hcRepeats :Bool!
    var hcTimer :NSTimer!
    var valid : Bool {
        get {
            if hcTimer != nil  {
                return hcTimer.valid
            } else {
                return false
            }
        }
    }

    func pausableScheduledTimerWithTimeInterval(ti: NSTimeInterval, target aTarget: AnyObject, selector aSelector: Selector, userInfo: AnyObject?, repeats yesOrNo: Bool) {

        let hcInstanceTimer = NSTimer.scheduledTimerWithTimeInterval(ti,target: aTarget, selector: aSelector, userInfo: userInfo, repeats: yesOrNo)

        self.hcTimerInterval = ti
        self.hcTarget = aTarget
        self.hcSelector = aSelector
        self.hcUserInfo = userInfo!
        self.hcRepeats = yesOrNo
        self.hcTimer = hcInstanceTimer  
    }

    func pause() {
        self.hcTimer.invalidate()
    }

    func resume() {
        self.hcTimer = NSTimer.scheduledTimerWithTimeInterval(self.hcTimerInterval, target: self.hcTarget, selector: self.hcSelector, userInfo: self.hcUserInfo, repeats: self.hcRepeats)
    }
}

Solution

  • Don't create a subclass of NSTimer, create a wrapper which exposes the functionality you need. You already have some confusion in your code about the hcTimer and what it is and is used for (I expect this hasn't become evident yet), your wrapper can also avoid that.

    So, your wrapper will optionally contain an NSTimer instance. Never call NSTimer() - this is effectively what your code is currently doing. You should either have no timer or you should have a fully created and scheduled timer instance.