[英]Closure does not run in the main thread in swift
我想在UITableViewCell上显示一些图像。 但是我遇到了fatal error: Index out of range
错误以下的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
}
在DispatchQueue.main.async{ ... }
包装要在主队列上运行的任何内容。
也就是说,您当前的方法可能无法奏效。 这个方法被大量调用。 当用户滚动时,每当一个单元格即将出现在屏幕上时,就会调用此方法(在iOS 10中,有时会在它出现在屏幕上之前)。 单元格经常被回收,并且每次请求单元格时,您都会将数据附加到titleArray
和其他数组(它们可能不是有序的;它们可能已经被取出;这个数组不会在右边结束订购)。
您需要将有关单元格的所有数据移动到模型对象中,并移出视图控制器。 不应该有titleArray
和urlArray
等。应该只有一篇Article
, Article
应该注意获取自己并更新其属性。 此方法的作用是从缓存中获取正确的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
}
}
然后有一些模型出现这些:
class Model {
func article(at index: Int) -> Article {
if let article = lookupArticleInCache(at: index) {
return article
}
let article = createAndCachePlaceholderArticle(at: index)
article.refresh()
}
}
然后你的代码看起来像:
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
}
您可以使用KVO或Swift Observables或ArticleDelegate
协议让单元格观察Article
。 当Article
更新时,单元格会自动更新。
同样,有很多方法可以解决这个问题。 您可以拥有所有单元格共享的“PlaceHolderArticle”,当真正的文章进入时,单元格会替换整个内容(因此文章是不可变的而不是自我更新的)。 您可以使用Swift Talk描述的更通用的方法。 有很多方法。 但关键是有这个模型可以独立于任何特定的UI更新自身,以及观察模型并显示其所拥有的UI(视图,视图控制器)。
如果您想要更多关于此主题的信息,请搜索“Massive View Controller”。 这是您目前正在使用的反模式的通用名称。 有很多方法可以解决这个问题,所以不要认为你在其上阅读的任何特定文章是“正确的方式”(人们已经提出了一些非常复杂,过于精细的解决方案)。 但所有这些都是基于将模型与UI分离。
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)
}
}
你必须为这个计算单独的功能,并尝试避免“ cellForRowAt ”中的任何计算功能
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.