简体   繁体   English

Swift数组未填写完成块

[英]Swift array not filled in completion block

I am trying to append array(simple task I know) but for some reason my array is empty. 我试图追加数组(我知道一个简单的任务),但是由于某种原因我的数组为空。 No matter what I do. 不管我做什么。 This is what I do: 这是我的工作:

I want to calculate the total balance(I should do amount * price and then sum them up). 我想计算总余额(我应该先做金额*价格,然后将其加总)。

So I thought I would make array of balances and then sum them up but the array is empty: 所以我以为我会制作一个余额数组,然后将它们加起来,但数组为空:

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)
    }

This is the getPrice function(it returns the price, I tested): 这是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)
                }
            }
        }
    }

So my question is, why is it empty? 所以我的问题是,为什么它是空的? If I am correct then the completion should have the filled array. 如果我是正确的,那么完成应该具有填充数组。 And there shouldn't be any thread problems. 而且不应该有任何线程问题。

Can somebody please lead me to the right track. 有人可以引导我走上正确的道路吗? Any response is appreciated! 任何回应表示赞赏!

getPrice is asynchronous. getPrice是异步的。 The for loop finishes long before the first call to getPrice even gets started. for循环早在首次调用getPrice之前getPrice完成。

You need to use DispatchGroup to ensure that the call to completion(balancesArray) isn't made until all of the calls to getPrice have completed. 您需要使用DispatchGroup来确保在对getPrice所有调用都完成之前不对completion(balancesArray)进行调用。 See What's the best way to iterate over results from an APi, and know when it's finished? 请参阅什么是最好的方法来遍历APi的结果,并知道何时完成? for an example. 举个例子

You will also need to ensure you append to balancesArray from a single thread to avoid concurrent writes. 您还需要确保从单个线程追加到balancesArray以避免并发写入。 See Create thread safe array in swift for solutions. 请参阅快速创建线程安全数组以获取解决方案。

The issue is that getPrice is asynchronous, so when you call completion your async functions haven't actually finished execution. 问题是getPrice是异步的,因此当您调用completion异步函数实际上尚未完成执行。 You can use a DispatchGroup to solve your issue. 您可以使用DispatchGroup解决您的问题。

You should also make sure that you only write to balancesArray from a single thread to avoid concurrency issues. 您还应确保仅从单个线程写入balancesArray ,以避免并发问题。

Below code ensures that the completion handler of getBalances is only called once all calls to getPrice are finished, but it doesn't make the balancesArray threadsafe, you can take care of that as explained in Create thread safe array in Swift 下面的代码确保完成处理getBalances一次,所有来电只叫getPrice完成,但它不会使balancesArray线程安全的,你可以照顾,由于在解释斯威夫特创建线程安全数组

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)
    })
}

Your getPrice function is flawed. 您的getPrice函数有缺陷。 You need to make sure that the completion handler is called in all cases even when the result is an error. 您需要确保在所有情况下都调用完成处理程序,即使结果是错误的也是如此。 When working with network requests, you should always make completion handlers return optionals and in case of any issues, you should call completion with a nil value. 处理网络请求时,应始终使完成处理程序返回可选值,并且在出现任何问题的情况下,应使用nil值调用completion If you want to know the reason for the nil value, you can also make your closure return a tuple containing an optional value and an optional Error , such as URLSession.dataTask(with:, completionHandler:) does. 如果您想知道使用nil值的原因,还可以使您的闭包返回一个包含可选值和可选Error的元组,例如URLSession.dataTask(with:, completionHandler:) , 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)
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM