调度队列不等待

Dispatch queue not waiting

情况是我需要遍历多个对象并对每个对象进行网络调用。

所有网络调用完成后,我将使用从这些网络调用中收集的所有数据调用完成块。

为此,我尝试使用调度组,在网络呼叫开始时进入,在网络呼叫结束时离开:

  for user in users {
    dispatch_group_enter(downloadGroup)
    UserManager.retrieveFriendsForUser(user, completed: { (usersFriends, fault) in
      guard let usersFriends = usersFriends else {
        return
      }
      dispatch_group_leave(downloadGroup)
    })
  }

然后我在等待他们完成以下内容:

dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER);

但是,它似乎并没有等待我所有的分组网络调用完成。

这是完整上下文中的所有代码;我正在遍历许多用户,然后查询他们的朋友。我希望该方法 return 他们所有的朋友合并(通过将所有结果连接到 allNonFriends 数组):

class func retrieveNonFriendsOfFriends(completed : (users : [BackendlessUser]?, fault : Fault?) -> Void) {

  var allNonFriends = [BackendlessUser]()
  let downloadGroup = dispatch_group_create() // 2

  dispatch_group_enter(downloadGroup) // 3
  UserManager.retrieveCurrentUsersFriends { (users, fault) in
    guard let users = users else {
      print("Server reported an error: \(fault)")
      completed(users: nil, fault: fault)
      return
    }
    for user in users {
      dispatch_group_enter(downloadGroup) // 3
      UserManager.retrieveFriendsForUser(user, completed: { (usersFriends, fault) in
        guard let usersFriends = usersFriends else {
          print("Server reported an error: \(fault)")
          return
        }
        let nonFriends = usersFriends.arrayWithoutFriends(users)
        allNonFriends += nonFriends
        dispatch_group_leave(downloadGroup) // 4
      })
    }
  }

  dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER);
  completed(users: allNonFriends, fault: nil)
}

每次调用dispatch_group_enter(),都需要调用dispatch_group_leave();如果您调用 dispatch_group_enter() 5 次,那么您还需要调用 dispatch_group_leave() 5 次。

您为检索当前用户的好友调用了 1 次

dispatch_group_enter(downloadGroup) // 3
UserManager.retrieveCurrentUsersFriends

然后#users 在完成后调用

for user in users {
  dispatch_group_enter(downloadGroup) // 3

问题 1

您没有平衡第一个检索当前用户好友的调用。

问题 2

如果 userFriends 为零,您永远不会为该用户调用 dispatch_group_leave()

最终的结果是你永远不会完成 dispatch_group 并且永远等待。

解决方案

dispatch_group_enter(downloadGroup) // 3
UserManager.retrieveCurrentUsersFriends { (users, fault) in
    defer {
        // Whether we return early or use the users we want to leave the group
        // Balances the initial enter()
        dispatch_group_leave(downloadGroup)
    }
    ... process users
    for user in users {
         dispatch_group_enter(downloadGroup) // 3
         UserManager.retrieveFriendsForUser(user, completed: { (usersFriends, fault) in
              // No matter the outcome of the call we want to leave the
              // dispatch_group so we don't wait forever
              defer {
                   // Balances the enter() for each user
                   dispatch_group_leave(downloadGroup)
              }
              .... process userFriends
         }
    }

推荐阅读:http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to-wait-for-multiple-web-services/