定时器 and/or RunLoop 的错误?

Bug with Timer and/or RunLoop?

在开始我正在进行的项目之前,我过去曾对此进行过广泛的测试。我将代码复制并粘贴到我的新项目中,所以它应该可以工作。我想让它做的就是每分钟重新加载 tableView,以便重新加载 tableView 上显示的时钟。

func setMinutesForReload(){
    
    //get calender object
    let calender = Calendar.current
    //curent date
    let now = Date()
    //create an array of dates to store minutes
    var minutes = [Date]()
    //timer array to store timers
    var timers = [Timer]()
    
    //set a run loop
    for i in 0...23{
        for j in 0...59{
            minutes.append(calender.date(bySettingHour: i, minute: j, second: 0, of: now)!)
            timers.append(Timer(fireAt: minutes[j], interval: 0, target: self, selector: #selector(minutelyReloadTimer), userInfo: nil, repeats: false))
            RunLoop.main.add(timers[j], forMode: RunLoop.Mode.common)
        }
    }
    
} 

@objc func minutelyReloadTimer(){
    self.cityTableView.reloadData()
}

奇怪的是,当我 运行 应用程序并设置断点时,我看到它立即调用 minutelyReloadTimer(),连续 59 次(我数过)。

这是最近 Xcode/Swift 更新的问题,还是我遗漏了一些我没有看到的东西?

我确定您看到的意外行为不是由于 Xcode/Swift 的问题。

查看您的代码,我立即注意到一些奇怪的事情。首先,RunLoop.main.add(timers[j]... 只会将计时器的 0-59 添加到 运行 循环中,一遍又一遍(准确地说是 24 次)。对于两个,calender.date(bySettingHour: i, minute: j, second: 0, of: now) 的默认配置是查找下一次匹配给定数据组件的时间,因为函数的最后一个参数(即 matchingPolicy: Calendar.MatchingPolicy)被分配了默认值 .nextTime .因此,返回的日期是明天,而不是今天。

现在,我不会进一步剖析您的(不必要的复杂)方法,而是建议一种更简单的方法:重复启动 60 秒计时器,并在下一分钟开始启动。

简而言之,重构您的代码会产生以下结果。

func setMinutesForReload(){
    
    //get calender object
    let calender = Calendar.current
    //curent date
    let now = Date()
    
    let dateOfNextMinute: Date = calender.nextDate(after: now, matching: DateComponents(second: 0), matchingPolicy: .nextTime)!
    let timer = Timer(fireAt: dateOfNextMinute, interval: 60.0, target: self, selector: #selector(minutelyReloadTimer), userInfo: nil, repeats: true)
    RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
}

@objc func minutelyReloadTimer(){
    print("Reload")
}