Ios 使用DispatchGroup时阻止主线程的异步调用

Ios 使用DispatchGroup时阻止主线程的异步调用,ios,swift,firebase,google-cloud-firestore,grand-central-dispatch,Ios,Swift,Firebase,Google Cloud Firestore,Grand Central Dispatch,我正在尝试从FireStore数据库获取文档。我需要先加载这些文档,然后才能继续执行我的功能。以下是代码供参考: 调用FireStore服务功能的视图控制器: let service = FirestoreServices() service.get(cottage: "test123456789") { model in nextViewController.cottageModel = model! sel

我正在尝试从FireStore数据库获取文档。我需要先加载这些文档,然后才能继续执行我的功能。以下是代码供参考:

调用FireStore服务功能的视图控制器:

    let service = FirestoreServices()
    service.get(cottage: "test123456789") { model in
        
        nextViewController.cottageModel = model!
        self.present(nextViewController, animated:true, completion:nil)
        
    }
正在调用的FireStore服务方法:

func get(cottage: String, completionHandler: @escaping (CottageTrip?) -> ()) {
    
    //get a reference to the firestore
    let db = Firestore.firestore()
    
    //get the references to the collections
    let cottageRef = db.collection("cottages").document(cottage)
    
    //get the fields from the initial document and load them into the cottage model
    cottageRef.getDocument { (document, error) in
        if let document = document, document.exists {
            
            //create the cottage model to return
            let cottageModel: CottageTrip = CottageTrip()
            
            //get the subcollections of this document
            let attendeesCollection = cottageRef.collection("attendees")
            //other collections
            
            //here I get all info from initial document and store it in the model
            
            let group = DispatchGroup()
            
            print("here")
            
            group.enter()
            
            //get the attendees
            DispatchQueue.global(qos: .userInitiated).async {
                
                attendeesCollection.getDocuments() { (querySnapshot, err) in
                    print("here2")
                    if let err = err {
                            print("Error getting documents: \(err)")
                    } else {
                        //get data
                    }
                    group.leave()
                }
                
            }
            
            print("after async call")
            
            //wait here until the attendees list is built
            group.wait()
            
            print("after wait")
            
            //create the cars
            carsCollection.getDocuments() { (querySnapshot, err) in
                print("in car collection get doc call")
                if let err = err {
                        print("Error getting documents: \(err)")
                } else {
                    //get car data
                    }
                }
            }
            
            //this is where she should block until all previous get document operations complete
            
            completionHandler(cottageModel)
            
        } else {
            print("Document does not exist")
            completionHandler(nil)
        }
        
    }
    
}

我意识到,
print(“here2”)
从未打印过,因此它似乎阻塞了
组。wait()
。我需要使用
group.wait()
而不是notify,因为我需要此函数来访问子集合和文档,因为我需要这些子集合和文档的值。我在网上阅读了很多答案,大多数人在这种情况下使用
group.wait()
,但由于某些原因,我无法在不锁定和冻结应用程序的情况下让它为我工作。

正如algrid指出的,您出现死锁,因为您正在等待主线程,Firestore需要调用其闭包

一般来说,避免调用
wait
,这样就不会死锁。使用
notify
,只需在
notify
闭包中调用闭包即可

因此,例如,假设您不需要
与会者的结果来查询
车辆
,您可以使用
通知
调度组模式,例如

func-get(小屋:字符串,completionHandler:@escaping(CottageTrip?)->Void){
设db=Firestore.Firestore()
let cottageRef=db.collection(“小屋”).document(小屋)
cottageRef.getDocument{document,出现错误
guard let document=文档,document.else{
打印(“文档不存在”)
completionHandler(无)
返回
}
让cottageModel=CottageTrip()
让AttendesCollection=cottageRef.collection(“与会者”)
let carsCollection=cottageRef.collection(“汽车”)
let group=DispatchGroup()
group.enter()
attendeesCollection.getDocuments(){querySnapshot,出现错误
延迟{group.leave()}
...
}
group.enter()
carsCollection.getDocuments(){querySnapshot,出现错误
延迟{group.leave()}
...
}
通知组(队列:.main){
completionHandler(cottageModel)
}
}
}
另外,顺便提一下,您不必向全局队列分派任何内容,因为这些方法已经是异步的


如果您需要一个结果来启动下一个,您可以嵌套它们。这将更慢(因为您放大了网络延迟效应),但也完全消除了对组的需要:

func-get(小屋:字符串,completionHandler:@escaping(CottageTrip?)->Void){
设db=Firestore.Firestore()
let cottageRef=db.collection(“小屋”).document(小屋)
cottageRef.getDocument{document,出现错误
guard let document=文档,document.else{
打印(“文档不存在”)
completionHandler(无)
返回
}
让cottageModel=CottageTrip()
让AttendesCollection=cottageRef.collection(“与会者”)
let carsCollection=cottageRef.collection(“汽车”)
attendeesCollection.getDocuments(){querySnapshot,出现错误
...
carsCollection.getDocuments(){querySnapshot,出现错误
...
completionHandler(cottageModel)
}
}
}
}

无论如何,我可能倾向于将其分解为单独的函数,因为它有点复杂,但想法是一样的。

调用wait()时,它会阻塞主线程,直到调用leave()。但是它永远不会被调用,因为getDocuments()的完成闭包也需要在主线程上运行。您在那里遇到了死锁。我看不出根据您提供的代码使用调度组有任何意义,因为似乎为创建平房模型而执行的所有任务都是串行的。首先获取文档,然后查询子集合,然后执行
x
,然后执行
y
。所有这些都已经是串行的,所以只需像您那样嵌套它们,如果在任何时候出现故障(如丢失的文档或网络错误),请在完成处理程序中返回
nil
,然后继续。@bsod这两个
getDocuments
调用是异步的,因此如果您想让它们同时运行,您确实需要团队成员知道何时完成这两项工作。但是,如果您需要一个为另一个的结果,那么您是正确的,您可以通过嵌套它们来逐个运行,从而消除对组的需要。