我试图在 for in 循环中运行一个函数 X 次,但是当所有函数都返回时,我想运行另一个函数。
目前我通过将最终功能延迟 1 秒来让它工作,但我真的想让 Dispatch Group 工作。
我已经通过各种在线示例和其他问题,但我尝试的似乎没有任何效果,我知道我目前拥有的代码不会工作,因为它每次运行 dispatchGroup.leave() 时 for in 函数发送而不是返回时。
我已经尝试将 DispatchGroup 代码放入函数中(位于另一个文件中),但我被难住了,但我认为我已接近解决方案。
我还查看了信号量并使用计数并在每次循环运行时递增一个值,但我一直回到 DispatchGroups。
我最后的办法是问一个问题!
View Controller 代码
@IBAction func removeDeviceBtn(_ sender: Any) {
let dispatchGroup = DispatchGroup()
for owner in arrOwnerList {
dispatchGroup.enter()
self.removeDevice(device: self.device, account: owner as! String, completion: self.completed)
dispatchGroup.leave()
}
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
self.removeDeviceFromServer(device: self.device)
self.sendEmail(to:"gordon@example.co.uk", subject:self.device+" has been removed", text:self.device+" has been removed from the server, please check the sim for bar and termination")
})
其他文件中的函数代码作为扩展
func completed(isSuccess: Bool) {
}
func removeDevice(device: String, account: String, completion: @escaping (Bool) -> Void) {
let dictHeader : [String:String] = ["username":Username,"password":Password]
let dictArray = [device]
WebHelper.requestPUTAPIRemoveDevice(BaseURL+"rootaccount/removedevices/"+account+"?server=MUIR", header: dictHeader, dictArray: dictArray, controllerView: self, success: { (response) in
if response.count == 0 {
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.ServerError, on: self)
}
}
else {
if response.count != 0 {
let isSuccess = true
completion(isSuccess)
}
else{
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.NoDataFound, on: self)
}
}
}
}) { (error) in
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: error?.localizedDescription ?? Messages.ServerError, on: self)
}
}
}
WebHelper 文件中的代码
class func requestPUTAPIRemoveDevice(_ strURL: String,header: Dictionary<String,String>,dictArray: Array<Any>, controllerView viewController: UIViewController, success: @escaping (_ response: [AnyHashable: Any]) -> Void, failure: @escaping (_ error: Error?) -> Void) {
if GlobalConstant.isReachable() {
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.showHUD()
}
let loginString = String(format: "%@:%@", header["username"]!, header["password"]!)
let loginData: Data = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString(options: NSData.Base64EncodingOptions())
let headers = ["Authorization": "Basic "+base64LoginString, "Referer": "http://www.example.com"]
let postData = try? JSONSerialization.data(withJSONObject: dictArray, options: [])
let request = NSMutableURLRequest(url: NSURL(string: strURL)! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = postData
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.hideHUD()
}
failure(error)
} else {
if let httpResponse = response as? HTTPURLResponse {
print("Server code \(httpResponse.statusCode)")
if httpResponse.statusCode == 200 || httpResponse.statusCode == 208 {
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.hideHUD()
}
let jsonResult = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
if (jsonResult is NSDictionary) {
success(jsonResult as! [AnyHashable : Any])
}
else if (jsonResult is NSArray) {
success(["response":jsonResult as! NSArray])
}
else{
success(["response":httpResponse.statusCode])
DispatchQueue.main.async {
}
}
}
else{
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.hideHUD()
}
failure(error)
}
}
}
})
dataTask.resume()
}
else {
DispatchQueue.main.async {
LoadingIndicatorView.sharedInstance.hideHUD()
GlobalConstant.showAlertMessage(withOkButtonAndTitle: "", andMessage: "Internet not connected", on: viewController)
}
}
}
最终的解决方案(除了整理其他各种问题)是将 success(["response":httpResponse.statusCode])
添加到 WebHelper 文件,更正了上面的代码
最佳答案
将 leave
放在完成处理程序中:
for owner in arrOwnerList {
dispatchGroup.enter()
removeDevice(device: device, account: owner as! String) { [weak self] success in
self?.completed(isSuccess: success)
dispatchGroup.leave()
}
}
或者假设你在 completed
函数中没有真正做任何事情,我只是删除它:
for owner in arrOwnerList {
dispatchGroup.enter()
removeDevice(device: device, account: owner as! String) { _ in
dispatchGroup.leave()
}
}
我注意到您在 removeDevice
中有不调用完成处理程序的执行路径。确保每条执行路径都调用完成处理程序,否则您的调度组将永远无法解析。
func removeDevice(device: String, account: String, completion: @escaping (Bool) -> Void) {
let dictHeader = ["username": Username, "password": Password]
let dictArray = [device]
WebHelper.requestPUTAPIRemoveDevice(BaseURL+"rootaccount/removedevices/"+account+"?server=MUIR", header: dictHeader, dictArray: dictArray, controllerView: self, success: { response in
DispatchQueue.main.async {
if response.count == 0 {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.ServerError, on: self)
completion(false)
} else {
completion(true)
}
}
}, failure: { error in
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: error?.localizedDescription ?? Messages.ServerError, on: self)
completion(false)
}
})
}
顺便说一句,我不知道“failure”闭包的名称,所以我假设它是failure
,但根据您的requestPUTAPIRemoveDevice
方法进行调整.我们通常会避免 Swift 中的多重闭包模式,但如果你要这样做,我会避免尾随闭包语法。它使第二个闭包的功能意图更加明确。
或者,这一切都回避了为什么 requestPUTAPIRemoveDevice
会启动 UI 更新的问题。我可能会把它放在 View Controller 方法中。所以 requestPUTAPIRemoveDevice
应该只传回足够的信息,以便 removeDeviceBtn
例程知道要呈现什么错误。这种为每次失败显示单独的错误消息的想法可能也值得怀疑。 (例如,如果你失去了互联网连接并试图删除一打设备,你真的想显示一打单独的错误消息吗?)但这超出了这个问题的范围。
关于swift - Dispatch Group 命令在代码中放在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55939799/