使用完成处理程序结束循环的方法?
Method For Ending A Loop With Completion Handler?
也许我在理解总体前提方面存在潜在问题,但我正在尝试找出最佳方法来对一系列项目做一些事情,然后结束 测试一旦找到某个标准就早点。
例如,我有一个名字数组;
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 THE LOOP。出于本示例的目的,我只关心第一次响应为真时(如果 Billy 存在于数组中,我对测试 Sarah、Brandon、Brian 或 Rick 没有进一步的兴趣)。
谢谢!
在开始循环之前,设置一个标志:
var exitEarly = false
for name in names {
每次通过循环测试标志:
for name in names {
if exitEarly {
break
}
在 TestName 响应块中,设置标志:
TestName(name) { response in
if response {
exitEarly = true
} else {
// KEEP GOING
}
}
但是请注意,如果 TestName 的块是异步执行的,那将不起作用,因为整个循环先于任何异步块的调用(这是异步性的本质)。
在TestName的闭包参数中添加@noescape
属性,表示闭包不逃逸调用
在TestName调用之外使用一个变量,设置为false,如果要停止,在循环内部设置为true。
调用TestName后,检查变量是否需要中断
或
- 将 TestName 更改为 return 一个指示是否应该继续的值
你的情况并不是设计循环的真正目的。循环可能会在循环内的闭包执行之前完成。
相反,尝试使用带有完成块的递归函数:
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)
}
}
}
这确保一次只发生一个调用,而不管响应块的同步性如何
我不得不和 PEEJWEEJ 做同样的事情。我的异步任务是 Web 查询和解析并且非常密集,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
}
}
}
也许我在理解总体前提方面存在潜在问题,但我正在尝试找出最佳方法来对一系列项目做一些事情,然后结束 测试一旦找到某个标准就早点。
例如,我有一个名字数组;
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 THE LOOP。出于本示例的目的,我只关心第一次响应为真时(如果 Billy 存在于数组中,我对测试 Sarah、Brandon、Brian 或 Rick 没有进一步的兴趣)。
谢谢!
在开始循环之前,设置一个标志:
var exitEarly = false
for name in names {
每次通过循环测试标志:
for name in names {
if exitEarly {
break
}
在 TestName 响应块中,设置标志:
TestName(name) { response in
if response {
exitEarly = true
} else {
// KEEP GOING
}
}
但是请注意,如果 TestName 的块是异步执行的,那将不起作用,因为整个循环先于任何异步块的调用(这是异步性的本质)。
在TestName的闭包参数中添加
@noescape
属性,表示闭包不逃逸调用在TestName调用之外使用一个变量,设置为false,如果要停止,在循环内部设置为true。
调用TestName后,检查变量是否需要中断
或
- 将 TestName 更改为 return 一个指示是否应该继续的值
你的情况并不是设计循环的真正目的。循环可能会在循环内的闭包执行之前完成。
相反,尝试使用带有完成块的递归函数:
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)
}
}
}
这确保一次只发生一个调用,而不管响应块的同步性如何
我不得不和 PEEJWEEJ 做同样的事情。我的异步任务是 Web 查询和解析并且非常密集,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
}
}
}