Ответ 1
Проблема
Как вы сказали, звонки в dispatch_group_enter
и dispatch_group_leave
должны быть сбалансированы. Здесь вы не можете сбалансировать их, потому что функция, выполняющая фактические запросы только в режиме реального времени, уходит.
Способ 1 - все вызовы в группе
Если вы не беспокоитесь о том, что myFirebaseFunction
всегда выполняет свою работу над этой группой отправки, тогда вы можете поместить оба входа и выйти туда, возможно, с помощью beginHandler и completeHandler:
func loadStuff() {
myFirebaseFunction(beginHandler: {
dispatch_group_enter(group)
dispatch_group_notify(group, dispatch_get_main_queue()) {
print("done")
}
}, completionHandler: { dispatch_group_leave(group) })
}
func myFirebaseFunction(beginHandler: () -> (), completionHandler: () -> ()) {
let usersRef = firebase.child("likes")
usersRef.observeEventType(.Value, withBlock: { snapshot in
beginHandler()
if snapshot.exists() {
let sorted = (snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key: "date",ascending: false)])
for item in sorted {
dict.append(item as! NSDictionary)
}
}
completionHandler()
})
}
Здесь обработчик завершения по-прежнему будет установлен на dispatch_group_leave
на loadStuff
, но есть также обработчик начала, который вызовет dispatch_group_enter
, а также dispatch_group_notify
. Причина, по которой нужно уведомлять, должна быть вызвана в начале, так это то, что мы должны убедиться, что мы уже вошли в группу, прежде чем мы вызываем уведомление, или блок уведомления будет выполняться немедленно, если группа пуста.
Блок, который вы передаете dispatch_group_notify
, вызывается только один раз, даже если блоки выполняются в группе после вызова уведомления. Из-за этого может быть безопасным, чтобы каждый автоматический вызов observeEventType
выполнялся в группе. Затем в любое время вне этих функций вам нужно дождаться завершения загрузки, вы можете просто вызвать уведомление.
Изменить:. Поскольку уведомление вызывается каждый раз, когда вызывается beginHandler
, этот метод фактически приведет к тому, что каждый раз будет вызываться блок уведомлений, поэтому он может быть не идеальным выбором.
Метод 2 - Только первый вызов группы, несколько методов
Если вам действительно нужен только первый вызов observeEventType
для использования этой группы, тогда один из вариантов должен иметь две версии myFirebaseFunction
: один, как тот, который у вас уже есть, и один с помощью observeSingleEventOfType
, Тогда загрузочный материал мог вызывать обе эти функции, передавая dispatch_group_leave
как завершение одному из них:
func loadStuff() {
dispatch_group_enter(group)
myInitialFirebaseFunction() {
dispatch_group_leave(group)
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
print("done")
}
myFirebaseFunction({})
}
func myInitialFirebaseFunction(completionHandler: () -> ()) {
let usersRef = firebase.child("likes")
usersRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
processSnapshot(snapshot)
completionHandler()
})
}
func myFirebaseFunction(completionHandler: () -> ()) {
let usersRef = firebase.child("likes")
usersRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
processSnapshot(snapshot)
completionHandler()
})
}
func processSnapshot(snapshot: FDataSnapshot) {
if snapshot.exists() {
let sorted = (snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key: "date",ascending: false)])
for item in sorted {
dict.append(item as! NSDictionary)
}
}
}
Метод 3 - Только первый вызов по группе, без дополнительных методов
Обратите внимание, что поскольку loadStuff
в "Методе 2" в основном загружает вещи из Firebase дважды, это может быть не так эффективно, как вам хотелось бы. В этом случае вместо этого вы можете использовать Bool
, чтобы определить, должен ли вызываться вызов:
var shouldLeaveGroupOnProcess = false
func loadStuff() {
dispatch_group_enter(group)
shouldLeaveGroupOnProcess = true
myFirebaseFunction() {
if shouldLeaveGroupOnProcess {
shouldLeaveGroupOnProcess = false
dispatch_group_leave(group)
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
print("done")
}
}
func myFirebaseFunction(completionHandler: () -> ()) {
let usersRef = firebase.child("likes")
usersRef.observeEventType(.Value, withBlock: { snapshot in
if snapshot.exists() {
let sorted = (snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key: "date",ascending: false)])
for item in sorted {
dict.append(item as! NSDictionary)
}
}
completionHandler()
})
}
Здесь, даже если во время начальной загрузки выполняются несколько вызовов observeEventType
, leave
гарантируется, что он будет вызван только один раз, и не произойдет сбой.
Swift 3
PS В настоящее время я использую Swift 2.3, но обновление до Swift 3 запланировано, поэтому было бы очень здорово получить ответ, способный для обоих.
Диспетчер получил полный пересмотр в Swift 3 (он объектно-ориентированный), поэтому код, который хорошо работает на обоих, на самом деле не вещь:)
Но понятия каждого из трех вышеперечисленных методов одинаковы. В Swift 3:
- Создайте свою группу с одним из элементов
DispatchGroup
-
dispatch_group_enter
теперь является методом экземпляраenter
в группе -
dispatch_group_leave
теперь является методом экземпляраleave
в группе -
dispatch_group_notify
теперь является методом экземпляраnotify
в группе