2016-04-13 2 views
2

Возможно, у меня есть основная проблема, понимающая общую предпосылку, но я пытаюсь найти наилучший подход к сделать что-то с массивом элементов и закончить тест раньше, когда будут найдены определенные критерии.Метод для завершения цикла с обработчиком завершения?

Например, у меня есть массив имен;
var names = ["Bob", "Billy", "Sarah", "Brandon", "Brian", "Rick"]

Я хотел бы протестировать каждое имя в массиве и посмотреть, существует ли он в базе данных. Для этого я вызываю другую функцию с обработчиком завершения;

for name in names { 

    TestName(name) { response in 

    if response { 
     // END THE LOOP 
    } else { 
     // KEEP GOING 
    } 
} 

Я не смог выяснить // END Петля. Для целей этого примера я беспокоюсь только тогда, когда ответ верен в первый раз (если Билли существует в массиве, я больше не заинтересован в тестировании Сары, Брэндона, Брайана или Рика).

Спасибо!

+0

Извините, о моем плохом ответе. Проблема в том, что вы находитесь в блоке, который я не заметил. :) – matt

+0

Кстати, если обработчик завершения TestName асинхронен, у вас есть основная проблема с вашей общей предпосылкой! – matt

+0

FYI, если это предназначено для синхронности, вы не должны использовать закрытие, и если он предназначен для асинхронности, вы не можете рассчитывать на свое поведение, используя цикл for – PeejWeej

ответ

3

Перед тем, как начать цикл, установить флаг:

var exitEarly = false 
for name in names { 

Test флаг каждый раз через петлю:

for name in names { 
    if exitEarly { 
     break 
    } 

В блоке ответа АСМАП, установить флаг:

TestName(name) { response in 
    if response { 
     exitEarly = true 
    } else { 
     // KEEP GOING 
    } 
} 

Обратите внимание, что если блок TestName выполняется асинхронно, это не сработает, поскольку весь цикл предшествует вызову любого из асинхронных блоков (это характер асинхронности).

+0

Это именно то, что я искал. Он работал для моих нужд; Мне было трудно теоретизировать, как сломать петлю, но это сработало хорошо. – ZbadhabitZ

+0

Мэтт, в вашем втором кодовом блоке выше вы не хотели сказать 'для имени в именах {if exitEarly {break}}', а не 'if name in names ...'? –

+0

, не будучи «неправильным» с очевидным прецедентом, предполагая, что закрытие будет выполняться синхронно, это довольно плохое предположение/привычка – PeejWeej

0
  1. Добавьте атрибут @noescape для параметра закрытия АСМАП, чтобы указать, что закрытие не избежать вызова.

  2. Используйте переменную вне вызова TestName, установите значение false и установите значение true в цикле, если вы хотите остановить.

  3. После вызова TestName проверьте переменную, чтобы увидеть, нужно ли вам сломать.

или

  1. Изменение АСМАП, чтобы вернуть значение, указывающее, должен ли он продолжать или не
1

Ваш случай не совсем то, что петля предназначена. Возможно, цикл может завершиться до того, как будут выполнены замыкания в цикле.

Вместо этого попробуйте рекурсивную функцию с блоком завершения:

func databaseHasAName(names: [String], index: Int, completion: (String?) ->()) { 

    guard index < names.count else{ 
     completion(nil) 
     return 
    } 

    let name = names[index] 
    TestName(name) { response in 

     if response { 
      completion(name) 
     } else { 
      databaseHasName(names, index: index + 1, completion: completion) 
     } 
    } 
} 

Это гарантирует, что только один вызов происходит в то время, независимо от синхронности блока ответа

0

я должен был сделать то же самое, что и PEEJWEEJ. Мои асинхронные задачи - это веб-запросы, синтаксический анализ и довольно интенсивный процесс, и цикл for всегда заканчивается до того, как эти задачи сделали так, что не было возможности их остановить.

Поэтому я преобразовал его в рекурсивный и установил флаг, когда захотел, чтобы он остановился, и теперь он отлично работает.

// ************************************************************************************************************ 
// MARK: Recursive function 
// I changed the loop to be recursive so I could stop when I need to - i.e. when selecting another race 
// I set the stopFetching flag in the seque when changing races and the current set of BG queries stops 
// ************************************************************************************************************ 
func getRaceResultsRecursive(race: Race, bibs: [Bib], index: Int, completion: @escaping (Bool?) ->()) 
{ 
    guard index < bibs.count else{ 
     completion(nil) 
     return 
    } 

    var url: URL 
    self.stopFetching = false 

    let bibID = bibs[index] 
     url = self.athleteResultAPI.formatURL(baseURL: (race.baseURL)!, 
               rd: (race.rdQueryItem)!, 
               race: (race.raceQueryItem)!, 
               bibID: "\(bibID.bib!)", 
      detail: (race.detailQueryItem)!, 
      fragment: (race.detailQueryItem)!, 
      webQueryType: (race.webQueryType!)) 

    self.athleteResultAPI.fetchAthleteResults(url: url, webQueryType: race.webQueryType!) 
    { 
     (athleteReturnCode) in 
     switch athleteReturnCode 
     { 
     case let .success(athleteResult): 
      self.athleteResults.append(athleteResult)  // Add to collection 
      self.delegate?.athleteResultsUpdated()   // update Delegate to notify of change 
     case let .failure(error): 
      print(error) 
      break 
     } 

     if self.stopFetching { 
      completion(true) 
     } 
     else { 
      self.getRaceResultsRecursive(race: race, bibs: bibs, index: index + 1, completion: completion) 
     } 
    } 
} 

// ************************************************************************************************************ 
// getRaceResults 
// Get all the bibs to track for this race from the iCloudKit database 
// ************************************************************************************************************ 
func getRaceResults(race: Race) 
{ 
    // get all the races and bibs an put in the Races Store 
    raceStore.fetchBibsForRace(race: race, foreignKey: race.recordID) 
    { 
     (results, error) in 
     if let error = error { 
      print("Error in fetchBibsForRace(): \(error)") 
      return 
     } 

     // clear the store since we are on a new race 
     self.athleteResults.removeAll() 
     self.getRaceResultsRecursive(race: race, bibs: race.bibs, index: 0) 
     { 
      (stopFetching) in 
      return 
     } 

    } 
} 
Смежные вопросы