简体   繁体   English

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

[英]Closure does not run in the main thread in swift

I want to show some images on UITableViewCell. 我想在UITableViewCell上显示一些图像。 However I got an error below fatal error: Index out of range . 但是我遇到了fatal error: Index out of range错误以下的fatal error: Index out of range The problem is that closure does not run in the main thread probably. 问题是闭包可能不会在主线程中运行。 How can I solve this issue? 我该如何解决这个问题?

 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
}

Wrap anything you want to run on the main queue in DispatchQueue.main.async{ ... } . DispatchQueue.main.async{ ... }包装要在主队列上运行的任何内容。

That said, your current approach likely won't work. 也就是说,您当前的方法可能无法奏效。 This method gets called a lot . 这个方法被大量调用。 While the user is scrolling, this method gets called every time a cell is about to come on the screen (in iOS 10, sometimes a bit before it'll come on the screen). 当用户滚动时,每当一个单元格即将出现在屏幕上时,就会调用此方法(在iOS 10中,有时会在它出现在屏幕上之前)。 Cells are often recycled, and you're appending data to the titleArray and other arrays every time a cell is requested (they may not be in order; they might have already been fetched; this array isn't going to wind up in the right order). 单元格经常被回收,并且每次请求单元格时,您都会将数据附加到titleArray和其他数组(它们可能不是有序的;它们可能已经被取出;这个数组不会在右边结束订购)。

You need to move all your data about a cell into a model object and out of the view controller. 您需要将有关单元格的所有数据移动到模型对象中,并移出视图控制器。 There shouldn't be a titleArray and an urlArray , etc. There should just be an Article , and the Article should take care of fetching itself and updating its properties. 不应该有titleArrayurlArray等。应该只有一篇ArticleArticle应该注意获取自己并更新其属性。 And the job of this method is to fetch the correct Article from your cache, or create a new one if needed, and assign it to an ArticleCell . 此方法的作用是从缓存中获取正确的Article ,或者根据需要创建新文章,并将其分配给ArticleCell The ArticleCell should watch the Article and update itself any time the Article changes (ie when the fetch completes). ArticleCell应该观看Article并在Article更改时(即获取完成时)自行更新。 Almost no work should happen directly in this method since it gets called so often, and in possibly random orders. 几乎没有任何工作应该直接在这个方法中发生,因为它经常被调用,并且可能是随机的命令。

The common way to build this kind of thing is with a simple model object (often a reference type so it can be observed; there are many other approaches that allow a struct, but they're a little more advanced so we'll keep this simple): 构建这种东西的常用方法是使用一个简单的模型对象(通常是一个引用类型,因此可以观察到;还有许多其他方法允许结构,但它们更高级,所以我们将保留这个简单):

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

Then there's some kind of Model that vends these: 然后有一些模型出现这些:

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

        let article = createAndCachePlaceholderArticle(at: index)
        article.refresh()
    }
}

And then your code looks like: 然后你的代码看起来像:

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
}

You can use KVO or Swift Observables or an ArticleDelegate protocol to let the cell observe the Article . 您可以使用KVO或Swift ObservablesArticleDelegate协议让单元格观察Article When the Article updates, the cell updates itself. Article更新时,单元格会自动更新。

Again, there are many ways to approach this. 同样,有很多方法可以解决这个问题。 You could have a "PlaceHolderArticle" that all the cells share and when the real Article comes in, the cell replaces the whole thing (so that Articles are immutable rather than self-updating). 您可以拥有所有单元格共享的“PlaceHolderArticle”,当真正的文章进入时,单元格会替换整个内容(因此文章是不可变的而不是自我更新的)。 You could use the more generic approaches described by Swift Talk . 您可以使用Swift Talk描述的更通用的方法。 There are lots of ways. 有很多方法。 But the key is that there is this model that updates itself, independent of any particular UI, and a UI (views, view controllers) that watch the model and display what it holds. 但关键是有这个模型可以独立于任何特定的UI更新自身,以及观察模型并显示其所拥有的UI(视图,视图控制器)。

If you want much, much more on this topic, search for "Massive View Controller." 如果您想要更多关于此主题的信息,请搜索“Massive View Controller”。 That's the common name for the anti-pattern you're currently using. 这是您目前正在使用的反模式的通用名称。 There are lots of ways to fight that problem, so don't assume that any particular article you read on it is "the right way" (people have come up with some very elaborate, and over-elaborate, solutions). 有很多方法可以解决这个问题,所以不要认为你在其上阅读的任何特定文章是“正确的方式”(人们已经提出了一些非常复杂,过于精细的解决方案)。 But all of them are based on separating the model from the UI. 但所有这些都是基于将模型与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)
    }
}

you have to make separate function for this calculation and try to avoid the any calculate functionality in " cellForRowAt " 你必须为这个计算单独的功能,并尝试避免“ cellForRowAt ”中的任何计算功能

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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