Ios 使用DispatchGroup时阻止主线程的异步调用
我正在尝试从FireStore数据库获取文档。我需要先加载这些文档,然后才能继续执行我的功能。以下是代码供参考: 调用FireStore服务功能的视图控制器: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
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
调用是异步的,因此如果您想让它们同时运行,您确实需要团队成员知道何时完成这两项工作。但是,如果您需要一个为另一个的结果,那么您是正确的,您可以通过嵌套它们来逐个运行,从而消除对组的需要。