Swift数组未填写完成块

时间:2018-02-07 15:45:12

标签: swift asynchronous

我正在尝试追加数组(我知道的简单任务)但由于某种原因我的数组是空的。不管我做什么。这就是我的工作:

我想计算总余额(我应该计算金额*价格,然后将它们相加)。

所以我想我会制作一系列余额然后总结它们但是数组是空的:

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

所以我的问题是,为什么它是空的?如果我是正确的,那么完成应该有填充的数组。并且不应该存在任何线程问题。

有人可以带我走上正轨。任何回应表示赞赏!

2 个答案:

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