Ios Swift-在单独的文件中使用函数中的多线程

Ios Swift-在单独的文件中使用函数中的多线程,ios,swift,multithreading,Ios,Swift,Multithreading,我试图在Swift中使用多线程来检索和显示视图控制器中的图像。但是,我用于检索图像的函数位于单独的模型中。我可以将该函数复制并粘贴到视图控制器中,但我将多次重用该函数,并希望将其与特定的视图控制器分开 我目前拥有的函数(同样,在我命名为WikimediaAPI的单独文件中)如下所示: public func getThumbnailImage(forPage page: String) -> UIImage? { if let data = try? Data(conten

我试图在Swift中使用多线程来检索和显示视图控制器中的图像。但是,我用于检索图像的函数位于单独的模型中。我可以将该函数复制并粘贴到视图控制器中,但我将多次重用该函数,并希望将其与特定的视图控制器分开

我目前拥有的函数(同样,在我命名为WikimediaAPI的单独文件中)如下所示:

public func getThumbnailImage(forPage page: String) -> UIImage? {
        if let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) {
            let json = JSON(data: data)
            if let pageId = json["query"]["pages"].dictionary?.first?.key {
                if let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string {
                    if let url = URL(string: imageUrl) {
                        if let imageData = try? Data(contentsOf: url) {
                            if let image = UIImage(data: imageData) {
                                return image
                            }
                        }
                    }
                }
            }
        }
        return nil
    }
上面的方法可以工作,但不是多线程的,当我从表视图控制器切换到视图控制器时,需要的时间太长。我尝试在这些方面实现多线程,但不允许在异步块中返回任何内容

public func getThumbnailImage(forPage page: String) -> UIImage? {
        DispatchQueue.global().async {
            if let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) {
                DispatchQueue.main.async {
                    let json = JSON(data: data)
                    if let pageId = json["query"]["pages"].dictionary?.first?.key {
                        if let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string {
                            if let url = URL(string: imageUrl) {
                                DispatchQueue.global().async {
                                    if let imageData = try? Data(contentsOf: url) {
                                        DispatchQueue.main.async {
                                            if let image = UIImage(data: imageData) {
                                                return image //error: "Unexpected non-void return value in void function"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return nil
    }
我曾经认为,可以使用异步代码块而不是UIImage返回闭包,但不确定如何实现这一点。如果我这样做了,我仍然需要在视图控制器中设置图像,我不知道该怎么做

以下是对视图控制器中函数的调用:

let wikiQuery = WikimediaAPI() // the class from above

fileprivate func setImage() {
        if let pageTitle = wikimediaPageTitleDict[(allergy.0).lowercased()] {
            if let image = wikiQuery.getThumbnailImage(forPage: pageTitle) {
                wikipediaImage.image = image
                wikipediaImage.contentMode = .scaleAspectFit
            }

        }

  }
我正在使用plist中的一个字典(wikimediaPageTitleDict)将一个字符串与右边的wikipedia页面相关联(allergy.0就是这个字符串)


谢谢你的帮助。谢谢大家!

如果使用异步代码,则无法从函数返回结果。异步不是这样工作的。异步函数调用将结果排队等待稍后获取。然后,该函数在结果可用之前返回

使用异步函数通常要传入一个完成块,当结果可用时调用该块。例如,您可以传入一个闭包,将新下载的映像安装到视图控制器中相应的映像视图中

请看我在这条线上的答案


我给出了一个使用完成处理程序的异步函数的工作示例。

在分派异步闭包中不能返回任何内容。 但是,您可以将UIImageView变量传递给
getThumbnailImage
当您收到图像时, 你可以马上设置

public func getThumbnailImage(forPage page: String, imageView : UIImageView) 
{
  ...
  ...
         if let imageData = try? Data(contentsOf: url) {
            DispatchQueue.main.async {
              if let image = UIImage(data: imageData) {
                imageView.image = image                                 
              }
            }
         }
}
和都为您执行此操作,它们扩展了UIImageView,因此您只需在图像视图上调用一个方法,当图像下载(或从缓存中检索)时,图像视图就会更新。我建议你用图书馆来做这个。但如果你想自己动手(注意守卫们要消灭厄运金字塔):


另外,上面的JSON引用了SwiftyJSON(),只是一个提示,如果让你的
进入相同的条件,你可以将你的
链接到相同的条件中,以避免死亡金字塔。好的一点,我忘记了Alamofire内置了异步映像加载。这是迄今为止最简单、最好的答案,您已经在使用AlamoFireTanks了!我最终使用了Alamofire,但演示代码确实有助于我理解正常情况下应该如何操作。另外,我还欣赏防护结构,没有意识到我可以做到这一点,但看起来更干净
    typealias ImageCallback = (UIImage?) -> Void

    func getThumbnailImage(forPage page: String, completion: @escaping ImageCallback) -> void {
        DispatchQueue.global().async {
            var imageToReturn: UIImage? = nil
            defer {
                completion(imageToReturn)
            }
            guard let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) else {
                return
            }
            let json = JSON(data: data)
            guard let pageId = json["query"]["pages"].dictionary?.first?.key,
                  let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string,
                  let url = URL(string: imageUrl) else {
                return
            }
            guard let imageData = try? Data(contentsOf: url),
                  let image = UIImage(data: imageData) else {
                return
            }
            imageToReturn = image
        }

    }

    getThumbnailImage(forPage: "Escherichia_coli") { image in
        DispatchQueue.main.async {
            imageView.image = image
        }
    }