Swift 4 个计时器因 NSException 而崩溃

Swift 4 Timer Crashes with NSException

我一直在寻找一种在 Swift 4 中使用计时器的方法,并查看了 this 文章。我在 xcode 中测试了我的代码,当计时器第一次计时(在本例中为 10 秒后)时,应用程序崩溃并且出现错误,尽管构建成功。

2017-11-20 19:54:42.781502-0700 Rock Prodigy[3022:554505] -[_SwiftValue   tick]: unrecognized selector sent to instance 0x608000051520
2017-11-20 19:54:42.791278-0700 Rock Prodigy[3022:554505] ***  Terminating app due to uncaught exception 'NSInvalidArgumentException',   reason: '-[_SwiftValue tick]: unrecognized selector sent to instance  0x608000051520'
*** First throw call stack:
(
0   CoreFoundation                      0x000000010360d1ab __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x0000000102ca2f41 objc_exception_throw + 48
2   CoreFoundation                      0x000000010368da34 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3   CoreFoundation                      0x00000001035900a8 ___forwarding___ + 1432
4   CoreFoundation                      0x000000010358fa88 _CF_forwarding_prep_0 + 120
5   Foundation                          0x000000010270e1ee __NSFireTimer + 83
6   CoreFoundation                      0x000000010359d2a4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
7   CoreFoundation                      0x000000010359cf62 __CFRunLoopDoTimer + 1026
8   CoreFoundation                      0x000000010359cb1a __CFRunLoopDoTimers + 266
9   CoreFoundation                      0x0000000103594534 __CFRunLoopRun + 2308
10  CoreFoundation                      0x00000001035939b9 CFRunLoopRunSpecific + 409
11  GraphicsServices                    0x00000001090e29c6 GSEventRunModal + 62
12  UIKit                               0x0000000103a885e8 UIApplicationMain + 159
13  Rock Prodigy                        0x000000010238b637 main + 55
14  libdyld.dylib                       0x0000000107aa1d81 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

这是我的 swift 4 码

import UIKit

class GeneralFitness: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    var timer: Timer! = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(tick), userInfo: nil, repeats: true)

    @objc func tick() {
        print("tick")
    }


}

如果您有任何问题,请告诉我。

您的计时器初始化方式错误,因为:

If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values.

此外:

You also cannot use the implicit self property, or call any of the instance’s methods (See here)

因此,要解决您的问题,您应该将此类代码移入 viewDidLoad。所以你可以试试这个:

import UIKit

class ViewController: UIViewController {
   weak var timer: Timer?

   override func viewDidLoad() {
      super.viewDidLoad()
      Timer.scheduledTimer(withTimeInterval: 10, repeats: true, block: { [weak self] timer in
         self?.timer = timer
         self?.tick()
      })
   }

   deinit { 
      self.timer?.invalidate() 
   }

   @objc func tick() {
      print("tick")
   }
}

作为, you should instantiate the timer in viewDidLoad. Also the documentation says:

The selector should have the following signature: timerFireMethod: (including a colon to indicate that the method takes an argument).

不要忘记在 viewDidDisappear 中禁用此计时器。你不能在 deinitinvalidate 因为重复计时器保持对其目标的强引用,只要计时器是 运行 你的 deinit 就不会被调用.如果您在 viewDidDisappear 中删除它,您可能希望在 viewDidAppear 中创建计时器。

因此,结果如下:

class ViewController: UIViewController {

    weak var timer: Timer?

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(tick(_:)), userInfo: nil, repeats: true)
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        timer?.invalidate()
    }

    @objc func tick(_ timer: Timer) {
        print("tick")
    }

}

或者您可以使用基于块的再现,使用 [weak self] 模式,计时器不会保持对视图控制器的强引用,在这种情况下您 可以 使用 deinit:

class ViewController: UIViewController {

    var timer: Timer?

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] timer in  // the `[weak self] reference is only needed if you reference `self` in the closure
            print("tick")
        }
    }

    deinit {
        timer?.invalidate()
    }

}

在 swift 4

中试试这个
import UIKit

class ViewController: UIViewController {

    var timer: Timer!

    override func viewDidLoad() {
       super.viewDidLoad()


        self.timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.updateValue), userInfo: nil, repeats: true)
    }

    func updateValue(){

        print("Timer is call")
    }
}