繁体   English   中英

Closure不会在swift的主线程中运行

[英]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和其他数组(它们可能不是有序的;它们可能已经被取出;这个数组不会在右边结束订购)。

您需要将有关单元格的所有数据移动到模型对象中,并移出视图控制器。 不应该有titleArrayurlArray等。应该只有一篇ArticleArticle应该注意获取自己并更新其属性。 此方法的作用是从缓存中获取正确的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 ObservablesArticleDelegate协议让单元格观察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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM