【问题标题】:Swift and Firebase: Closures race conditionSwift 和 Firebase:闭包竞争条件
【发布时间】: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


【解决方案1】:

我需要获取第二个闭包之外的主题数组 (self.fetchSubjects(....))。

这是不可能的:任何需要主题的代码都需要在闭包内,或者从那里调用。

如果您想在其他地方使用主题列表,您可以使用fetchSubjects 函数执行您所做的操作:传入一个回调,然后在加载主题时调用该回调。

另见这些其他questions about asynchronously loading data from Firebase in Swift

【讨论】:

    猜你喜欢
    • 2017-09-17
    • 2020-07-12
    • 1970-01-01
    • 2017-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    相关资源
    最近更新 更多