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

时间:2017-02-09 14:41:36

标签: ios swift multithreading

我想在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
}

2 个答案:

答案 0 :(得分:3)

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

尽管如此,您当前的方法可能无法奏效。此方法称为 lot 。当用户滚动时,每当一个单元格即将出现在屏幕上时,就会调用此方法(在iOS 10中,有时会在它出现在屏幕上之前)。单元通常被回收,并且每次请求单元时,您都会将数据附加到titleArray和其他数组(它们可能不是有序的;它们可能已经被取出;这个数组不会被卷绕正确的顺序)。

您需要将有关单元格的所有数据移动到模型对象中,并移出视图控制器。不应该有titleArrayurlArray等。应该只有ArticleArticle应该负责获取自己并更新其属性。此方法的工作是从缓存中获取正确的Article,或者根据需要创建一个新的ArticleCell,并将其分配给ArticleCellArticleArticle观看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
}

然后有某种模型出现这些:

ArticleDelegate

然后您的代码如下:

Article

您可以使用KVO或Swift ObservablesArticle协议让细胞观察using (Stream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None)) { MimeKit.MimeMessage mimeMessage = MimeKit.MimeMessage.Load(stream); int i = 1; foreach (MimeKit.MimePart attachment in mimeMessage.Attachments) { if (attachment.GetType() == typeof(MimeKit.Tnef.TnefPart)) { MimeKit.Tnef.TnefPart tnefPart = (MimeKit.Tnef.TnefPart)attachment; MimeKit.MimeMessage tnefMessage = tnefPart.ConvertToMessage(); tnefMessage.WriteTo(path + $"_tnefPart{i++}.eml"); } } } 。当Attachments更新时,单元格会自行更新。

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

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

答案 1 :(得分:0)

 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)
    }
}

您必须为此计算单独执行功能,并尝试避免&#34; cellForRowAt &#34;中的任何计算功能。