等待某个任务完全完成以执行另一个任务

时间:2019-05-03 18:29:52

标签: swift firebase asynchronous grand-central-dispatch

我正在尝试从Firebase实时数据库中检索一些加密消息,对其进行解密,然后将其显示在CollectionView中。解密过程是成功的,但是我遇到了有关多线程的问题:添加到Messages数组中的已解密消息的顺序错误,因此它们不会以正确的顺序显示在CollectionView中,即显示消息的顺序在每次运行中,CollectionView中的值会有所不同。我认为发生此问题是因为完成每个加密消息的解密过程所需的时间不同,一些加密消息需要更多的时间来解密,而某些加密消息先于其他消息完成解密,因此将它们添加到Messages数组的顺序为不再正确。我期望的工作流程:

  1. 对Firebase数据库上的message节点进行提取请求
  2. 对于每条提取的消息:
    3.1。解密
    3.2。将其追加到Messages数组
    3.3。重新加载CollectionView以更新UI

但是我不知道如何使用GCD来正确实现,由于并发问题,显示的消息顺序不正确。但是,我找到了一个解决方案,如果尝试在我的代码中放置sleep(1)命令,该代码可以正确运行,但是由于睡眠命令,它太慢了。我尝试了很多方法,但是除了使用sleep(1)命令之外,它似乎并不正确。请帮助我正确执行此操作,非常感谢!这是我的代码:

func observeMessage(){ 
        self.eThree = VirgilHelper.sharedVirgilHelper.eThreeToUse!

        // Get current user's UID
        guard let uid = FIRAuth.auth()?.currentUser?.uid , let toId = self.user?.id else {
            return;
        }

        let userMessagesRef = FIRDatabase.database().reference().child("user-messages").child(uid).child(toId);
        userMessagesRef.observe(.childAdded, with: { (snapshot) in

            let messageId = snapshot.key;
            let messagesRef = FIRDatabase.database().reference().child("messages").child(messageId);
            // Observe the entire value of that node
            messagesRef.observeSingleEvent(of: .value, with: { (snapshot) in

                if let dictionary = snapshot.value as? [String:AnyObject] {

                //sleep(1) // The working sleep command, but it's too slow

                let message = Message(dictionary: dictionary)
                    if let fromUID = message.fromId, let toUID = message.toId, let cipherText = message.text {

                        self.eThree!.lookupPublicKeys(of: [fromUID], completion: { (lookupResult, error) in
                            if error != nil {
                                print("Error when looking up the Public Key of UID \(fromUID), \(String(describing: error))")
                            }
                            if let lookupResult = lookupResult {
                                message.text = try! self.eThree!.decrypt(text: cipherText, from: lookupResult[fromUID]!)
                                print("text: \(message.text)")

                                // The concurency prolem happens at here
                                self.messages.append(message);

                                // Go back to main thread to update UI
                                DispatchQueue.main.async {
                                    // The concurency prolem happens at here, UI doesn't display with correct order of fetched-decrypted messages
                                    self.collectionView?.reloadData()

                                    let indexPath = IndexPath(item: self.messages.count-1, section: 0)
                                    self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true);
                                }
                            }

                        })
                    }
                }

            }, withCancel: nil)
        }, withCancel: nil)

    }

1 个答案:

答案 0 :(得分:0)

在Swift中,要等待其他任务完成后再继续执行其他任务,可以使用DispatchQueue.group()。

let group = DispatchGroup()

group.enter()
DispatchQueue.main.async {
    print("1")
    group.leave()
}

group.enter()
DispatchQueue.main.async {
    print("2")
    group.leave()
}

group.enter()
DispatchQueue.main.async {
    print("3")
    group.leave()
}

group.notify(queue: .main) {
    print("Done")
}

使用方式:

  1. 初始化群组
  2. 在开始任务之前通过以下方式输入分组:group.enter()
  3. 在每个任务之后放置:group.leave()
  4. 将闭包传递给group.notify。当组任务为空时将执行该程序。

注意:  许多.enter()需要与.leave()匹配