I'm relatively new to coding and I'm trying to switch a boolean value by with Timer
. However, I keep getting an error. Here's my code:
var Display = true
var BoolTimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(ThirdViewController.SwitchBool), userInfo: nil, repeats: true)
@objc func SwitchBool()
{
if Display == true{
Display = false
print(Display)
} else {
Display = true
print(Display)
}
}
I get this error when the timer runs out:
[_SwiftValue SwitchBool]: unrecognized selector sent to instance 0x604000646ed0 2018-02-13 10:55:35.664486+1300 HC 1[12286:466779] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue SwitchBool]: unrecognized selector sent to instance 0x604000646ed0' libc++abi.dylib: terminating with uncaught exception of type NSException (lldb)
Can someone help me understand why I am getting this error and how to fix it?
If you run this code, what would you expect the output to be?
import Foundation
class C: NSObject {
var foo = type(of: self)
}
print(C().foo)
You're expecting to see C
, right? But instead you get:
(C) -> () -> C
The reason for this is that when you use self
in a property's default value, self
refers to a closure which is created as part of the initialization process, not to the object itself. The reason for this is that the property default values are computed before the object's init
function has completed, and you can't use self
before the object has completed initialization. So, Objective-C is trying to send the SwitchBool
method to a closure, which of course doesn't support that method.
To fix this, just make the property lazy
; this will cause the property to be initialized sometime after the object itself has been initialized, which will make it possible to use self
and have it actually refer to your object, as you can see from the following test:
import Foundation
class C: NSObject {
lazy var foo = type(of: self)
}
print(C().foo)
which outputs:
C
EDIT: If the object in question is a view controller, initializing the timer to nil
and creating it in viewDidLoad
, as suggested by @vacawama, is also a good approach to take. Basically you just need to make sure you create the timer at some point after the object has completed its initialization.