Swift iOS-如何从管理循环中取消DispatchGroup() [英] Swift iOS -how to cancel DispatchGroup() from managing a loop

查看:44
本文介绍了Swift iOS-如何从管理循环中取消DispatchGroup()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遍历几个 Url ,将它们转换为 Data ,然后将数据发送到 Firebase Storage ,然后在完成所有操作后发送收集的信息到 Firebase数据库

I loop through several Urls, convert them to Data, then send the data to Firebase Storage and then when everything is done send the gathered info to Firebase Database

我使用DispatchGroup()的 .enter()开始循环,一旦将数据发送到Storage并获得值url字符串 absoluteString ,我就使用 .leave()开始下一次迭代.

I use DispatchGroup()'s .enter() to start the loop and once I send the data to Storage and obtain a value url string absoluteString I use .leave() to start the next iteration.

我意识到,在循环过程中,可能会发生一些错误:

What I realized is that during the loop, there are several points where errors can occur:

  1. UrlSession
  2. 内部
  3. 一次进入Storage的 .putData 函数
  4. 一次进入Storage的 .downloadURL(completion:... 完成处理程序
  5. ,如果最终的下载URL的 ?. absoluteString 为nil,则再次显示
  1. once inside the UrlSession
  2. once inside Storage's .putData function
  3. once inside Storage's .downloadURL(completion:... completion handler
  4. and again if the final downloadURL's ?.absoluteString is nil

如果在任何时候出现错误,我都会显示一个警报函数 showAlert(),该函数显示警报并使用 session.invalidateAndCancel()取消UrlSession.我取消了所有操作,因为我希望用户重新开始.

If I get an error at any of those points I show an alert function showAlert() that shows the alert and cancel's the UrlSession with session.invalidateAndCancel(). I cancel everything because I want the user to start all over again.

由于DispatchGroup()挂在 .enter()上,如何取消DispatchGroup()来停止循环?

Since the DispatchGroup() is left hanging at .enter(), how do I cancel the DispatchGroup() to stop the loop?

var urls = [URL]()
var picUUID = UUID().uuidString
var dict = [String:Any]()

let session = URLSession.shared
let myGroup = DispatchGroup()
var count = 0

for url in urls{

    myGroup.enter()
    session.dataTask(with: url!, completionHandler: {
            (data, response, error) in

            if error != nil { 
                self.showAlert() // 1st point of error
                return 
            }

            DispatchQueue.main.async{
                self.sendDataToStorage("\(self.picUUID)_\(self.count).jpg", picData: data)
                self.count += 1
            }
    }).resume()

    myGroup.notify(queue: .global(qos: .background) {
        self.sendDataFromDictToFirebaseDatabase()
        self.count = 0
        self.session.invalidateAndCancel()
   }
}

func sendDataToStorage(_ picId: String, picData: Data?){

    dict.updateValue(picId, forKey:"picId_\(count)")

    let picRef = storageRoot.child("pics")
    picRef.putData(picData!, metadata: nil, completion: { (metadata, error) in

        if error != nil{
            self.showAlert()  // 2nd point of error
            return
        }

        picRef?.downloadURL(completion: { (url, error) in

            if error != nil{
                self.showAlert()  // 3rd point of error
                return
            }

            if let picUrl = url?.absoluteString{

               self.dict.updateValue(picUrl, forKey:"picUrl_\(count)")
               self.myGroup.leave() //only leave the group if a Url string was obtained
            }else{
               self.showAlert()  // 4th point of error
            }
        })
    })
}

func showAlert(){
    // the DispatchGroup() should get cancelled here
    session.invalidateAndCancel()
    count = 0
    UIAlertController...
}

func sendDataFromDictToFirebaseDatabase(){
}

推荐答案

在下面的问题中,@ rmaddy说您需要致电 leave 是否成功".我这样做了,但是循环仍然运行,尽管有错误,但 sendDataFromDictToFirebaseDatabase()仍然触发了事件.

In the comments below the question @rmaddy said "You need to call leave whether is succeeds or not". I did that but the loop still ran and sendDataFromDictToFirebaseDatabase() still fired event hough there was an error.

我能找到的唯一解决方法是将循环放入带有完成处理程序的函数中,并使用 bool 来决定是否应 sendDataFromDictToFirebaseDatabase()火:

The only work around I could find was to put the loop inside a function with a completion handler and use a bool to decide wether or not the sendDataFromDictToFirebaseDatabase() should fire:

var urls = [URL]()
var picUUID = UUID().uuidString
var dict = [String:Any]()

let session = URLSession.shared
let myGroup = DispatchGroup()
var count = 0
var wasThereAnError = false // use this bool to find out if there was an error at any of the error points

func loopUrls(_ urls: [URL?], completion: @escaping ()->()){
    
    for url in urls{
        
        myGroup.enter()
        session.dataTask(with: url!, completionHandler: {
            (data, response, error) in
            
            if error != nil {
                self.showAlert() // 1st point of error. If there is an error set wasThereAnError = true
                return
            }
            
            DispatchQueue.main.async{
                self.sendDataToStorage("\(self.picUUID)_\(self.count).jpg", picData: data)
                self.count += 1
            }
        }).resume()
        
        myGroup.notify(queue: .global(qos: .background) {
            completion()
        }
    }
}

// will run in completion handler
func loopWasSuccessful(){
    
    // after the loop finished this only runs if there wasn't an error
    if wasThereAnError == false {
        sendDataFromDictToFirebaseDatabase()
        count = 0
        session.invalidateAndCancel()
    }
}

func sendDataToStorage(_ picId: String, picData: Data?){
    
    dict.updateValue(picId, forKey:"picId_\(count)")
    
    let picRef = storageRoot.child("pics")
    picRef.putData(picData!, metadata: nil, completion: { (metadata, error) in
        
        if error != nil{
            self.showAlert()  // 2nd point of error. If there is an error set wasThereAnError = true
            return
        }
        
        picRef?.downloadURL(completion: { (url, error) in
            
            if error != nil{
                self.showAlert()  // 3rd point of error. If there is an error set wasThereAnError = true
                return
            }
            
            if let picUrl = url?.absoluteString{
                
                self.dict.updateValue(picUrl, forKey:"picUrl_\(count)")
                self.myGroup.leave() // leave group here if all good on this iteration
            }else{
                self.showAlert()  // 4th point of error. If there is an error set wasThereAnError = true
            }
        })
    })
}

func showAlert(){
    wasThereAnError = true // since there was an error set this to true
    myGroup.leave() // even though there is an error still leave the group
    session.invalidateAndCancel()
    count = 0
    UIAlertController...
}

func sendDataFromDictToFirebaseDatabase(){
}

并使用它:

@IBAction fileprivate func postButtonPressed(_ sender: UIButton) {    

    wasThereAnError = false // set this back to false because if there was an error it was never reset

    loopUrls(urls, completion: loopWasSuccessful)
}

这篇关于Swift iOS-如何从管理循环中取消DispatchGroup()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆