我正在尝试追加数组(我知道的简单任务)但由于某种原因我的数组是空的。不管我做什么。这就是我的工作:
我想计算总余额(我应该计算金额*价格,然后将它们相加)。
所以我想我会制作一系列余额然后总结它们但是数组是空的:
func getBalances(completion: @escaping (_ balancesArray: [Double])->()){
var balancesArray = [Double]()
for f in portfolioArray{
getPrice(for: f.name!, in: "EUR", completion: { (price) in
balancesArray.append(f.amount * price)
//!!!!!!If I print here the array, it is full!!!!!!
})
}
completion(balancesArray)
}
这是getPrice函数(它返回价格,我测试过):
func getPrice(for crypto: String, in fiat: String, completion: @escaping (_ price: Double)->()){
API.getCryptoRates(cryptoSymbol: crypto, fiatSymbol: fiat) { (error, response: [CurrencyResponse]?) in
if error == nil{
if let prices = response{
guard let p = Double(prices[0].price) else { return }
completion(p)
}
}
}
}
所以我的问题是,为什么它是空的?如果我是正确的,那么完成应该有填充的数组。并且不应该存在任何线程问题。
有人可以带我走上正轨。任何回应表示赞赏!
答案 0 :(得分:2)
getPrice
是异步的。 <{1}}循环在第一次调用for
之前很久就会结束。
您需要使用getPrice
确保在完成对DispatchGroup
的所有调用之后才会调用completion(balancesArray)
。有关示例,请参阅What's the best way to iterate over results from an APi, and know when it's finished?。
您还需要确保从单个线程追加getPrice
以避免并发写入。有关解决方案,请参阅Create thread safe array in swift。
答案 1 :(得分:1)
问题是getPrice
是异步的,所以当你调用completion
时,你的异步函数实际上还没有完成执行。您可以使用DispatchGroup
来解决问题。
您还应该确保只从单个线程写入balancesArray
以避免并发问题。
下面的代码确保getBalances
的完成处理程序仅在所有对getPrice
的调用完成后调用,但它不会使balancesArray
线程安全,您可以处理正如Create thread safe array in Swift
func getBalances(completion: @escaping (_ balancesArray: [Double])->()){
var balancesArray = [Double]()
let group = DispatchGroup()
for f in portfolioArray{
group.enter()
getPrice(for: f.name!, in: "EUR", completion: { price in
if let p = price{
balancesArray.append(f.amount * p)
}
group.leave()
})
}
group.notify(queue: .main, execute: {
completion(balancesArray)
})
}
您的getPrice
功能存在缺陷。您需要确保在所有情况下都调用完成处理程序,即使结果是错误也是如此。处理网络请求时,您应始终使完成处理程序返回选项,如果出现任何问题,您应该使用completion
值调用nil
。如果您想知道nil
值的原因,您还可以使闭包返回包含可选值和可选Error
的元组,例如URLSession.dataTask(with:, completionHandler:)
。
func getPrice(for crypto: String, in fiat: String, completion: @escaping (_ price: Double?)->()){
API.getCryptoRates(cryptoSymbol: crypto, fiatSymbol: fiat) { (error, response: [CurrencyResponse]?) in
if error == nil{
if let prices = response{
guard let p = Double(prices[0].price) else { completion(nil); return }
completion(p)
} else {
completion(nil)
}
} else {
completion(nil)
}
}
}