Почему этот обработчик завершения пропускает цикл for - PullRequest
0 голосов
/ 20 января 2020

У меня есть эта группа кода, я хочу запустить только: self.performSegue ПОСЛЕ того, как все циклы for и все асинхронные задачи Firebase завершили работу:

    getFeaturedPost(withCompletion: startNext)

    func getFeaturedPost(withCompletion completion: () -> Void ) {
    print("Getting featured posts...")
    ref.child("featured").child("amount").observeSingleEvent(of: .value, with: { (snapshot) in
        self.numberOfPosts = snapshot.value as! Int

        print("There's \(self.numberOfPosts) posts avaliable.")

        for pos in 0..<self.numberOfPosts{
            print("Getting reference names for post: \(pos + 1)")
            self.ref.child("featured").child("post\(pos + 1)").observeSingleEvent(of: .value, with: { (snapshot) in
                let postID = (snapshot.value as? NSDictionary)?["postID"] as? String ?? ""
                let userOfPost = (snapshot.value as? NSDictionary)?["userOfPost"] as? String ?? ""
                self.customValues.append(("/users/public/\(userOfPost)/posts/\(postID)"))
            })
        }
    })
    print("Done! The posts are: \(customValues)")
    completion()
}

func startNext()
{
    getPostData(withCompletion: {
        print("Finished getting data, going to main screen.")
        self.performSegue(withIdentifier: "showHome", sender: nil)
    })
}

func getPostData(withCompletion completion: () -> Void ) {
    print("Getting idividual post data, there are \(customValues.count) posts")
    for i in 0..<customValues.count {
        print("Currently on post: \(i)")
        let encodedURL = (customValues[i] + "/postURL")
        ref.child(encodedURL).observe(.value, with: { (snapshot) in
            if let newURL = snapshot.value as? String{
                print("Sending \(newURL) to DemoSource Class")
                DemoSource.shared.add(urlString: newURL)
            }
        })
    }
    completion()
}

Все же startNext() Функция (которая переходит к следующему представлению) выполняется до того, как getFeaturedPost запускается, для l oop, где она печатает, в каком посте она находится c в данный момент. К концу, когда я отправляю данные в класс demosource с DemoSource.shared.add(urlString: newURL), значение newURL равно nil, у меня есть консольный журнал, который показывает вам порядок операторов печати каждой функции:

Getting featured posts...
Done! The posts are: []
Getting idividual post data, there are 0 posts
Finished getting data, going to main screen. // This line should be printed last meaning this function is being executed too early
There's 2 posts avaliable.
Getting reference names for post: 1 // These two lines should be called before the line 'Finished getting data'
Getting reference names for post: 2

Ответы [ 2 ]

3 голосов
/ 20 января 2020

Использование DispatchGroup очень просто. Это своего рода счетчик. enter увеличивает счетчик, leave уменьшает его. Если счетчик достигает 0, выполняется замыкание в notify.

  • В l oop перед асинхронным вызовом блока enter.
  • Внутри асинхронного блока в конце звоните leave.
  • После вызова l oop notify.

    func getFeaturedPost(withCompletion completion: @escaping () -> Void ) {
        print("Getting featured posts...")
        ref.child("featured").child("amount").observeSingleEvent(of: .value, with: { (snapshot) in
            self.numberOfPosts = snapshot.value as! Int
    
            print("There's \(self.numberOfPosts) posts avaliable.")
            let group = DispatchGroup()
            for pos in 0..<self.numberOfPosts{
                group.enter()
                print("Getting reference names for post: \(pos + 1)")
                self.ref.child("featured").child("post\(pos + 1)").observeSingleEvent(of: .value, with: { (snapshot) in
                    if let post = snapshot.value as? [String:Any] {
                        let postID = post["postID"] as? String ?? ""
                        let userOfPost = post["userOfPost"] as? String ?? ""
                        self.customValues.append(("/users/public/\(userOfPost)/posts/\(postID)"))
                    }
                    group.leave()
                })
            }
            group.notify(queue: .main) {
                print("Done! The posts are: \(customValues)")
                completion()
            }
        })
    }
    

Создать группу соответствующим образом в другом методе.

Примечание: не используйте NS... типы коллекций в Swift.

0 голосов
/ 20 января 2020

DispatchGroup

Группы позволяют объединять набор задач и синхронизировать поведение в группе. Вы присоединяете несколько рабочих элементов к группе и планируете их асинхронное выполнение в одной и той же очереди или в разных очередях. Когда все рабочие элементы завершают выполнение sh, группа выполняет свой обработчик завершения. Вы также можете синхронно ждать выполнения всех задач в группе до завершения sh. Документация Apple

Вы можете редактировать свои методы, используя DispatchGroup

 func getFeaturedPost(withCompletion completion: @escaping() -> Void ) {
      let group = DispatchGroup() // create group
        print("Getting featured posts...")
         group.enter() // enter group
        ref.child("featured").child("amount").observeSingleEvent(of: .value, with: { (snapshot) in
            self.numberOfPosts = snapshot.value as! Int

            print("There's \(self.numberOfPosts) posts avaliable.")

            for pos in 0..<self.numberOfPosts{
                  group.enter() // enter group
                print("Getting reference names for post: \(pos + 1)")
                self.ref.child("featured").child("post\(pos + 1)").observeSingleEvent(of: .value, with: { (snapshot) in
                    let postID = (snapshot.value as? NSDictionary)?["postID"] as? String ?? ""
                    let userOfPost = (snapshot.value as? NSDictionary)?["userOfPost"] as? String ?? ""
                    self.customValues.append(("/users/public/\(userOfPost)/posts/\(postID)"))
                     group.leave()
                })
            }
                group.leave() // 
        })
        print("Done! The posts are: \(customValues)")

    }
    group.notify(queue: .main, execute: { // executed after all async calls in for loop finish
        print("done with all async calls")
        // do stuff
        completion()
    })

func getPostData(withCompletion completion:@escaping () -> Void ) {
    print("Getting idividual post data, there are \(customValues.count) posts")
let group = DispatchGroup() // create group
    for i in 0..<customValues.count {
group.enter()
        print("Currently on post: \(i)")
        let encodedURL = (customValues[i] + "/postURL")
        ref.child(encodedURL).observe(.value, with: { (snapshot) in

            if let newURL = snapshot.value as? String{
                print("Sending \(newURL) to DemoSource Class")
                DemoSource.shared.add(urlString: newURL)
            }
             group.leave()
        })
    }
    group.notify(queue: .main, execute: { // executed after all async calls in for loop finish

            completion()
        })
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...