带开关的DispatchQueue.main.asyncAfter

时间:2019-03-09 17:26:06

标签: ios swift grand-central-dispatch

我整理了以下结构,以在网络连接速度慢时提醒用户。

当函数要调用服务器时,它会创建一个ResponseTimer。这将设置一个延迟通知,仅当responseTimer var isOn = true时才会触发。当我的函数从服务器返回响应时,设置responseTimer.isOn = false。

这是结构:

struct ResponseTimer {

var isOn: Bool

init() {
    self.isOn = true
    self.setDelayedAlert()
}

func setDelayedAlert() {
    let timer = DispatchTime.now() + 8
    DispatchQueue.main.asyncAfter(deadline: timer) {
        if self.isOn {
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: toastErrorNotificationKey), object: self, userInfo: ["toastErrorCase" : ToastErrorCase.poorConnection])
        }
    }
}

这就是我的使用方式

func getSomethingFromFirebase() {

    var responseTimer = ResponseTimer()

    ref.observeSingleEvent(of: .value, with: { snapshot in
        responseTimer.isOn = false

        //do other stuff
    })
}

即使在8秒延迟完成之前响应又回来了,通知仍会触发。我在这里做错了什么???是否有更好的模式可用于此类情况?

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

更好的方法是使用可以取消的DispatchSourceTimer

var timer : DispatchSourceTimer?

func startTimer()
{
    if timer == nil {
        timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
        timer!.schedule(deadline: .now() + .seconds(8))
        timer!.setEventHandler {
            DispatchQueue.main.async {
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: toastErrorNotificationKey), object: self, userInfo: ["toastErrorCase" : ToastErrorCase.poorConnection])
            }
            self.timer = nil
        }
        timer!.resume()
    } 
}

func getSomethingFromFirebase() {

    startTimer()

    ref.observeSingleEvent(of: .value, with: { snapshot in
         self.timer?.cancel()
         self.timer = nil
        //do other stuff
    })
}

答案 1 :(得分:2)

Timer是执行此操作的更好方法(为什么在收到响应并且知道您不再需要该任务之后,将已分派的任务保留在此处)。但是我看不到上面会导致您描述的行为的任何东西。我建议您在isOn上添加一些明智的日志记录和/或“监视”功能,以便查看它的设置/重置位置。

但是Timer实现可能像这样:

class ResponseTimer {
    private weak var timer: Timer?

    func schedule() {
        timer = Timer.scheduledTimer(withTimeInterval: 8, repeats: false) { _ in // if you reference `self` in this block, make sure to include `[weak self]` capture list, too
            NotificationCenter.default.post(...)
        }
    }

    func invalidate() {
        timer?.invalidate()
    }

    // you might want to make sure you `invalidate` this when it’s deallocated just in 
    // case you accidentally had path of execution that failed to call `invalidate`.

    deinit {
        invalidate()
    }
}

然后您可以这样做:

func getSomethingFromFirebase() {

    var responseTimer = ResponseTimer()
    responseTimer.schedule()

    ref.observeSingleEvent(of: .value) { snapshot in
        responseTimer.invalidate()

        //do other stuff
    }
}