微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

swift – 为什么我的DispatchGroup的等待总是超时?

我有这个代码
let goAheadAction = UIAlertAction(title: "Go Ahead",style: .default) { _ in
    let dispatchGroup = dispatchGroup()
    dispatchGroup.enter()

    Server.POSTRequest(accesspoint: "/UserServices/forgotPassword",params:["emailAddress" : self.emailAddresstextField.text!],handler: {
            (data: Data?,response: URLResponse?,error: Error?) -> Void in
        // Todo: Only considered successful case
        /*let confirmAlertController = UIAlertController(title: "Email been sent",message: "Please check your mailBox",preferredStyle: UIAlertControllerStyle.alert)

        self.present(confirmAlertController,animated: true,completion: nil)*/

        dispatchGroup.leave()
    })

    let waitResult = dispatchGroup.wait(timeout: dispatchTime(uptimeNanoseconds: 10000000000))

    if waitResult == .success {
        let emailSentAlertController = UIAlertController(title: "Email Sent",message: "We've sent an password reset link to \"\(self.emailAddresstextField.text!)\"",preferredStyle: UIAlertControllerStyle.alert)

        let gotitaction = UIAlertAction(title: "Got it",style: UIAlertActionStyle.cancel,handler: nil)

        emailSentAlertController.addAction(gotitaction)
        self.present(emailSentAlertController,completion: nil)
    } else {
        let timeOutAlertController = UIAlertController(title: "Time Out",message: "Failed to request service,reason: \"Time Out\"",handler: nil)

        timeOutAlertController.addAction(gotitaction)
        self.present(timeOutAlertController,completion: nil)
    }
}

每次我让dispatchGroup等待,它总是返回超时.怎么了?我把它设置为10000000000纳秒,这应该是10秒吧?我的要求确实在10秒之前回来了.

几点想法:

>正如rmaddy所说,如果你想等待10秒,正确的语法将是:

let waitResult = dispatchGroup.wait(timeout: .Now() + 10)

你的语法根本不会等待,看起来它会立即超时.
>你根本不应该等待.

首先,你永远不应该阻止主线程.在最坏的情况下,如果您在错误的时间执行此操作,您可能会被监视程序进程杀死.充其量,它是一个可怕的用户体验(即应用程序似乎完全冻结给最终用户).

其次,根据POSTRequest的实现方式,您可能会引入一个新问题.具体来说,如果POSTRequest在主队列上调用其完成处理程序(例如Alamofire等网络库的认行为),则可能会死锁,直到主线程上的等待超时到期为止.如果您要等待(无论如何都不应该这样做),您需要确保永远不要等待完成处理程序将使用的同一队列.

注意,如果完成处理程序没有在您正在等待的同一队列上运行,则没有死锁风险,但您仍然遇到我之前提到的问题,即您正在阻塞主线程,这应该始终是避免.

您应该在闭包内移动所有代码.如果该闭包在后台线程上运行,请确保将dispatchQueue.main.async {…}发送回主队列.

因此,正确的模式类似于以下内容.

>如果您希望请求在10秒后超时,请将其构建到原始请求中.注意timeoutInterval:

class func POSTRequest(accesspoint: String,params: [String: String]?,completionHandler: @escaping (Data?,URLResponse?,Error?) -> Void) {
    let url = URL(string: baseURLString)!
        .appendingPathComponent(accesspoint)

    let request = URLRequest(url: url,timeoutInterval: 10)

    URLSession.shared.dataTask(with: request,completionHandler: completionHandler)
}

显然,你可能在POSTRequest中做了其他事情,所以不要迷失在那里的细节.关键是在请求中构建timeoutInterval.
>然后,确保您的UIAlertAction启动请求并简单地将POSTRequest之后的所有处理放入其completionHandler中,从而无需等待任何调度组或类似的事情:

let goAheadAction = UIAlertAction(title: "Go Ahead",style: .default) { _ in
    let email = self.emailAddresstextField.text!
    let parameters = ["emailAddress": email]
    Server.POSTRequest(accesspoint: "/UserServices/forgotPassword",params: parameters) { data,response,error in
        dispatchQueue.main.async {
            guard error == nil else {
                let alert = UIAlertController(title: "Time Out",preferredStyle: .alert)
                alert.addAction( UIAlertAction(title: "Got it",style: .cancel))
                self.present(alert,animated: true)
                return
            }

            let alert = UIAlertController(title: "Email Sent",message: "We've sent an password reset link to \"\(email)\"",preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Got it",style: .cancel))
            self.present(alert,animated: true)
        }
    }
}

这样,没有线程被阻止.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐