【发布时间】:2020-12-11 06:21:18
【问题描述】:
我需要在闭包中调用闭包并避免竞争条件。
目前,我有一个名为 FirebaseUtilities 的类,其中包含与调用 Firebase 和检索数据有关的所有代码。
应用逻辑
一篇文章有很多数据,如标题、摘要、正文、主题等。主题使用此约定链接到文章:
在这个类中,我有一个获取文章的函数:
获取文章的关闭
func fetchArticles(completion: @escaping ([Article]?, Error?) -> Void) {
var subjects = [Subject]()
Database.database().reference().child("articles").observe(.value) { (snapshot) in
// in here i get the value of the snapshot and from there i extract the key of each subject and i store it in an array of String (ex. ["-MD4tN_AjPuCvTUEyZyL"])
// then I call another closure that is responsible for fetching the subjects based on the UID of each subject (see below)
}
})
获取主题的闭包
func fetchSubjects(subjectsUids: [String], completion: @escaping ([Subject]?, Error?) -> Void) {
var subjects = [Subject]()
subjectsUids.forEach { (subjectUid) in
self.dbSubjectsRef.child(subjectUid).observe(.value) { (snapshot) in
guard let subjectDictionary = snapshot.value as? [String: Any] else { return }
let subject = Subject(dictionary: subjectDictionary)
subjects.append(subject)
completion(subjects, nil)
}
}
}
问题
Subjects 数组将始终为空数组,因为它始终比从 Firebase 获取数据要快:
func fetchArticles(completion: @escaping ([Article]?, Error?) -> Void) {
var subjects = [Subject]()
Database.database().reference().child("articles").observe(.value) { (snapshot) in
// in here i get the value of the snapshot and from there i extract the key of each subject and i store it in an array of String (ex. ["-MD4tN_AjPuCvTUEyZyL"])
// then I call another closure that is responsible for fetching the subjects based on the UID of each subject
self.fetchSubjects(subjectsUids: subjectsUids) { (subjectsResults, error) in
guard let sub = subjectsResults else { return }
subjects = sub
// (A) printing here **shows desired results**
print(subjects)
}
// (B) printing here **does not show the results**
// it returns an empty array []
print(subjects)
}
})
我想要什么:
我需要获取第二个闭包之外的主题数组 (self.fetchSubjects(....))。
关于如何解决这种竞争条件的任何想法?
【问题讨论】:
-
这是问题// 然后我调用另一个闭包 - 为什么?你已经在第一个闭包中拥有了这些数据?为什么要返回并获取您已经拥有的数据? a)在第一个闭包中嵌套阅读主题,因此它仅在阅读文章后阅读,或者 b)在加载所有文章后使用 DispatchQueue 去阅读所有主题。不过,您再次为自己创造了许多额外的工作——只需在第一个闭包中嵌套阅读文章主题,就像您正在做的那样。那里没有竞争条件,因为数据在闭包内有效。
-
哦。目前尚不清楚你为什么要这样做
var subjects = [Subject](),但不要每次都初始化它——这会擦除数组。只需将数据附加到它,例如使它成为一个类数组。如果您需要示例代码,请告诉我。 -
@Jay,我没有第一个闭包中的数据,只有我想要的数据的 UID。要获取实际数据,我需要发起另一个 Firebase 调用并使用适当的 UID 来获取包含所有数据的对象。
-
澄清一下,在读取文章/主题 ID 的主函数中,您拥有所有文章数据,然后是您想要的主题 ID。所以,就在那里读吧。没有必要阅读所有文章然后单独阅读所有主题 - 这就是导致“竞争条件”的原因。或者,您可以使用 dispatch 将您的函数按顺序排列,但这可能有点矫枉过正。另外,我会再次提及,在您的函数中执行此
var subjects = [Subject]()将使主题仅存在于函数中。您应该填充类变量,而不是本地变量。 -
@Jay 我做 var subject = [Subject]() 是因为我需要将主题存储在某个地方,然后使用它们来构建我的文章结构。另外,我没有使用类 var,因为我创建了一个专用的 Firebase 实用程序类,我在视图控制器中调用它,如下所示:FirebaseUtilities().fetchArticles { ... }。通过这种方式,我可以非常轻松地维护 Firebase 的代码,而不是从一个 VC 转到另一个 VC 并更改代码。同样,我尝试为每个节点(主题、作者等)使用单独的闭包,并在 fetchArticles 闭包中使用这些闭包。希望它有一些意义。
标签: swift firebase firebase-realtime-database closures race-condition