关闭不会在主线程中快速运行

tek*_*kun 1 multithreading ios swift

我想在 UITableViewCell 上显示一些图像。但是我在下面遇到错误 fatal error: Index out of range。问题是闭包可能不会在主线程中运行。我该如何解决这个问题?

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "PickupTableViewCell", for: indexPath) as! PickupTableViewCell

    APIManager.getAnotherArticle{ (articles: Array<Article>?) in

        for info in articles! {
            self.authorArray.append(info.author)
            self.descriptionArray.append(info.description)
            if info.publishedAt != nil {
                self.publishedAtArray.append(info.publishedAt)
            }
            self.titleArray.append(info.title)
            self.urlArray.append(info.url)
            self.urlToImageArray.append(info.urlToImage)
            print(self.authorArray)
        }
    }

    let program = urlToImageArray[indexPath.row] //index out of range
    let urlToImage = NSURL(string: program)
    cell.pickupImageView.sd_setImage(with: urlToImage as URL!)

    return cell
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*ier 5

将您想要在主队列上运行的任何内容包装在DispatchQueue.main.async{ ... }.

也就是说,您目前的方法可能行不通。这个方法被调用了很多。当用户滚动时,每次单元格即将出现在屏幕上时都会调用此方法(在 iOS 10 中,有时会在它出现在屏幕上之前)。单元格经常被回收,titleArray每次请求单元格时,您都会将数据附加到和其他数组中(它们可能没有按顺序排列;它们可能已经被提取;这个数组不会以正确的顺序结束) )。

您需要将有关单元格的所有数据移入模型对象并移出视图控制器。不应该有 atitleArray和 anurlArray等。应该只有 an Article,并且 theArticle应该负责获取自身并更新其属性。这个方法的工作是Article从你的缓存中获取正确的,或者根据需要创建一个新的,并将它分配给一个ArticleCell. 本ArticleCell应该观看Article和更新本身的任何时间Article改变时(即当抓取完成)。几乎没有工作应该直接在这个方法中发生,因为它经常被调用,而且可能是随机的。

构建这种东西的常用方法是使用一个简单的模型对象(通常是一个引用类型,因此可以观察它;还有许多其他方法允许使用结构体,但它们更高级一些,因此我们将保留它简单的):

class Article {
    var author: String
    var description: String
    var publishedAt: Date
    var title: String
    var url: URL
    var image: UIImage
    func refresh() { 
       // fetch data from server and replace all the placeholder data
    }
}
Run Code Online (Sandbox Code Playgroud)

然后有某种模型可以出售这些:

class Model {
    func article(at index: Int) -> Article {
        if let article = lookupArticleInCache(at: index) {
            return article
        }

        let article = createAndCachePlaceholderArticle(at: index)
        article.refresh()
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你的代码看起来像:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "PickupTableViewCell", for: indexPath) as! PickupTableViewCell

    cell.article = sharedModel.article(at: indexPath.row)
    return cell
}
Run Code Online (Sandbox Code Playgroud)

您可以使用 KVO 或Swift ObservablesArticleDelegate协议让单元观察Article. 当Article更新时,单元更新自身。

同样,有很多方法可以解决这个问题。您可以拥有一个所有单元格共享的“PlaceHolderArticle”,当真正的文章进入时,单元格会替换整个内容(因此文章是不可变的而不是自我更新的)。您可以使用Swift Talk描述的更通用的方法。有很多方法。但关键是有这个模型可以自我更新,独立于任何特定的 UI,还有一个 UI(视图、视图控制器)可以观察模型并显示它所拥有的内容。

如果您想了解更多关于此主题的信息,请搜索“Massive View Controller”。这是您当前使用的反模式的通用名称。有很多方法可以解决这个问题,所以不要假设你读到的任何特定文章都是“正确的方法”(人们已经提出了一些非常精细和过度阐述的解决方案)。但所有这些都是基于将模型与 UI 分离。