从服务器下载图像时出现问题

Issue while downloading images from server

我从服务器收到了一个数组对象,然后我想下载那个对象上有一个 属性 的图像。然后我想用数组对象和图像(视图模型)更新 UI。我正在后台线程上下载图片,但我获取图片时出现延迟,而且对象根本没有填充,我做错了什么?

func presentCoinse(_ list: Home.Models.CoinseListResponse) {
        var coins = [Home.Models.coinsViewModel]()
        for item in list {
            getImage(symbol: item.symbol) { image in
                let i = Home.Models.coinsViewModel(image: image,
                                                   symbol: item.symbol,
                                                   name: item.name,
                                                   buyPrice: item.buyPrice,
                                                   sellPrice: item.sellPrice,
                                                   change24Hource: item.symbol)
                coins.append(i)
            }
        }
        viewController?.displayCoinsList(viewModel: coins)
    }

    private func getImage(symbol: String, complation: @escaping(_ image: Data?) -> Void) {
        
        queue.async {
            if let url = URL(string: "\(CDN_URL)\(symbol).png") {
              
                let data = try? Data(contentsOf: url)
                
                DispatchQueue.main.async {
                    complation(data)
                }
            }
        }
    }

请注意,您将硬币设置为空列表,然后使用该空列表调用 displayCoinstList。该列表是异步添加和更新的。 您应该在准备就绪时触发视图控制器重新加载硬币列表。我会在查看图像之前制作硬币列表,然后在收到图像后 - UIImageView 将自行呈现。

    func presentCoinse(_ list: Home.Models.CoinseListResponse) {

            // async part srarts here

            var coins = [Home.Models.coinsViewModel]()
            for item in list {
                getImage(symbol: item.symbol) { image in
                    let i = Home.Models.coinsViewModel(image: image,
                                                       symbol: item.symbol,
                                                       name: item.name,
                                                       buyPrice: item.buyPrice,
                                                       sellPrice: item.sellPrice,
                                                       change24Hource: item.symbol)
                    coins.append(i)
                }
            }

            // async part ends here

            viewController?.displayCoinsList(viewModel: coins)
        }

你所做的等同于:

func presentCoinse(_ list: Home.Models.CoinseListResponse) {
        var coins = [Home.Models.coinsViewModel]()
        viewController?.displayCoinsList(viewModel: coins) // coins in empty at this point)

        for item in list {
            getImage(symbol: item.symbol) { image in
                let i = Home.Models.coinsViewModel(image: image,
                                                   symbol: item.symbol,
                                                   name: item.name,
                                                   buyPrice: item.buyPrice,
                                                   sellPrice: item.sellPrice,
                                                   change24Hource: item.symbol)
                coins.append(i)
            }
        }
    }

getImage方法包含异步操作,在这种情况下闭包将在return方法之后调用。 并且,在每个异步 getImage.

之前调用方法 displayCoinsList(viewModel:)

您可以使用 DispatchGroup class.

func presentCoinse(_ list: Home.Models.CoinseListResponse) {
    var coins = [Home.Models.coinsViewModel]()
    let dispatchGroup = DispatchGroup() // add a dispatch group 
    
    for item in list {
        dispatchGroup.enter() // increment group counter before async call
        getImage(symbol: item.symbol) { image in
            defer {
                dispatchGroup.leave() // decrease group counter in every condition with 'defer'
            }
            let i = Home.Models.coinsViewModel(image: image,
                                               symbol: item.symbol,
                                               name: item.name,
                                               buyPrice: item.buyPrice,
                                               sellPrice: item.sellPrice,
                                               change24Hource: item.symbol)
            coins.append(i)
        }
    }
    dispatchGroup.notify(queue: .main) {
        // wait all async calls are completed
        viewController?.displayCoinsList(viewModel: coins)
    }
}

private func getImage(symbol: String, complation: @escaping(_ image: Data?) -> Void) {
    
    queue.async {
        if let url = URL(string: "\(CDN_URL)\(symbol).png") {
            let data = try? Data(contentsOf: url)
            complation(data)
        } else {
            complation(nil)
        }
    }
}

注意:在 getImage 中,方法需要在每个条件下调用 complation。如果您忘记调用它,DispatchGroup 将无法收到通知,您将被阻止。