我正在尝试从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")
从未打印过,所以它似乎挡住了group.wait()
。我需要使用group.wait()
而不是通知,因为我需要此函数只有在加载了attenders集合之后才能访问子集合和文档,因为我对子集合和文档需要这些值。我在网上读了很多答案,大多数人在这种情况下都使用group.wait()
,但由于某种原因,如果不锁定和冻结应用程序,我就无法让它为我工作。
正如algrid所指出的,您会出现死锁,因为您正在等待主线程,Firestore需要调用它的闭包。
一般来说,避免调用wait
,这样就不会出现死锁。使用notify
,只需在notify
闭包中调用闭包。
因此,例如,假设您不需要来自attendees
的结果来查询cars
,您可以只使用notify
调度组模式,例如
func get(cottage: String, completionHandler: @escaping (CottageTrip?) -> Void) {
let db = Firestore.firestore()
let cottageRef = db.collection("cottages").document(cottage)
cottageRef.getDocument { document, error in
guard let document = document, document.exists else {
print("Document does not exist")
completionHandler(nil)
return
}
let cottageModel = CottageTrip()
let attendeesCollection = cottageRef.collection("attendees")
let carsCollection = cottageRef.collection("cars")
let group = DispatchGroup()
group.enter()
attendeesCollection.getDocuments() { querySnapshot, err in
defer { group.leave() }
...
}
group.enter()
carsCollection.getDocuments() { querySnapshot, err in
defer { group.leave() }
...
}
group.notify(queue: .main) {
completionHandler(cottageModel)
}
}
}
此外,顺便说一句,但您不必向全局队列调度任何内容,因为这些方法已经是异步的。
如果你需要一个的结果来启动下一个,你可以嵌套它们。这将更慢(因为你放大了网络延迟效应(,但也完全消除了对群组的需求:
func get(cottage: String, completionHandler: @escaping (CottageTrip?) -> Void) {
let db = Firestore.firestore()
let cottageRef = db.collection("cottages").document(cottage)
cottageRef.getDocument { document, error in
guard let document = document, document.exists else {
print("Document does not exist")
completionHandler(nil)
return
}
let cottageModel = CottageTrip()
let attendeesCollection = cottageRef.collection("attendees")
let carsCollection = cottageRef.collection("cars")
attendeesCollection.getDocuments() { querySnapshot, err in
...
carsCollection.getDocuments() { querySnapshot, err in
...
completionHandler(cottageModel)
}
}
}
}
不管怎样,我可能倾向于将其分解为单独的函数,因为它有点多毛,但想法是一样的。