ios - 使用 DispatchQueue 索引超出范围异常

标签 ios swift grand-central-dispatch

我有以下代码,它在 UITableView 中显示了测验列表。 问题是为了显示图像,我调用了我的方法 prepareImages 并且我得到了一个索引在 tableView 函数中填充单元格时出现超出范围异常,因为 quizzesImages 数组似乎为空 (print(self.quizzesImages.count)显示 0),我知道这与我如何让线程工作有关,但我看不出哪里出错了。

import UIKit
 // Estructura del JSON que devuelve la URL
struct ResponseObject : Codable {
let quizzes : [Quiz]?
let pageno : Int?
    let nextUrl : String?
}

class QuizzesTableViewController: UITableViewController {

// Aquí se guardan los quizzes cargados de la URL
var totalQuizzes = [Quiz]()
var quizzesImages = [UIImage]()

override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.rowHeight = 90.0
    navigationController?.navigationBar.prefersLargeTitles = true
    downloadQuizzes()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func prepareImages(){
    for i in 0...self.totalQuizzes.count-1{

        let imageUrlString = self.totalQuizzes[i].attachment?.url
        let imageUrl:URL = URL(string: imageUrlString!)!
        print(imageUrl)

        // Start background thread so that image loading does not make app unresponsive
        DispatchQueue.global(qos: .userInitiated).async {
            let imageData:NSData = NSData(contentsOf: imageUrl)!

            // When from background thread, UI needs to be updated on main_queue
            DispatchQueue.main.async {
                let image = UIImage(data: imageData as Data)
                print("hola")
                self.quizzesImages.append(image!)
            }
        }
    }

}

func downloadQuizzes(){
    let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
    if let url = URL(string: QUIZZES_URL){
        let queue = DispatchQueue(label: "download quizzes queue")
        queue.async {
            DispatchQueue.main.async {
                UIApplication.shared.isNetworkActivityIndicatorVisible = true
            }
            defer{
                DispatchQueue.main.async {
                    UIApplication.shared.isNetworkActivityIndicatorVisible = false
                }
            }
            let data = try? Data(contentsOf: url, options: .alwaysMapped)
            let decoder = JSONDecoder()
            do{
                let response = try decoder.decode(ResponseObject.self, from: data!)
                DispatchQueue.main.async {
                    if (response.quizzes!.count != 0){
                        self.totalQuizzes.append(contentsOf: response.quizzes!)
                        self.prepareImages()
                        print(self.totalQuizzes.count)
                        print(self.quizzesImages.count)
                        self.tableView.reloadData()
                    }
                }
            }
            catch {
                print(error)
            }
        }
    }
}
// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return totalQuizzes.count
}

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

    let quiz = totalQuizzes[indexPath.row]
    let images = quizzesImages[indexPath.row]
    cell.authorLabel?.text = quiz.author?.username
    cell.quizLabel?.text = quiz.question
    cell.quizImage?.image = images
    return cell
}}

在此先感谢您的帮助!

最佳答案

See this screen shot form my playground

我尝试在 playground 中运行您的代码并设法让它工作并进行一些更改,请参阅以下内容。

Note

为了让它在 Playground 上运行,我必须做出一些假设,只需用我的核心逻辑替换您的核心逻辑即可。

编码愉快😀

import UIKit
// Estructura del JSON que devuelve la URL

struct ResponseObject: Codable {
    let quizzes: [Quiz]?
    let pageno: Int?
    let nextURL: String?

    enum CodingKeys: String, CodingKey {
        case quizzes, pageno
        case nextURL = "nextUrl"
    }
}

struct Quiz: Codable {
    let id: Int?
    let question: String?
    let author: Author?
    let attachment: Attachment?
    let favourite: Bool?
    let tips: [String]?
}

struct Attachment: Codable {
    let filename: String?
    let mime: MIME?
    let url: String?
}

enum MIME: String, Codable {
    case imageJPEG = "image/jpeg"
}

struct Author: Codable {
    let id: Int?
    let isAdmin: Bool?
    let username: String?
}


class QuizzesTableViewController: UITableViewController {

    // Aquí se guardan los quizzes cargados de la URL
    var totalQuizzes = [Quiz]()
    var quizzesImages = [UIImage]()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.rowHeight = 90.0
        navigationController?.navigationBar.prefersLargeTitles = true
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Quiz")
        self.tableView.dataSource = self
        self.tableView.delegate = self
        downloadQuizzes()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func prepareImages(){
        print("in prepare images")
        // Start background thread so that image loading does not make app unresponsive
        DispatchQueue.global(qos: .userInitiated).async {
            for i in 0...self.totalQuizzes.count-1 {

                let imageUrlString = self.totalQuizzes[i].attachment?.url
                let imageUrl:URL = URL(string: imageUrlString!)!
                print(imageUrl)

            let imageData:NSData = NSData(contentsOf: imageUrl)!
            let image = UIImage(data: imageData as Data)
            print("hola \(i)")
            self.quizzesImages.append(image!)
            print(self.quizzesImages.count)
            // When from background thread, UI needs to be updated on main_queue
            }
            DispatchQueue.main.async {
                print(self.quizzesImages.count)
                self.tableView.reloadData()
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
            }
        }
    }

    func downloadQuizzes(){
        let QUIZZES_URL = "https://quiz2019.herokuapp.com/api/quizzes?token=945d3bf7d4c709d69940"
        if let url = URL(string: QUIZZES_URL){
            let queue = DispatchQueue(label: "download quizzes queue")
            queue.async {
                DispatchQueue.main.async {
                    UIApplication.shared.isNetworkActivityIndicatorVisible = true
                }
                let data = try? Data(contentsOf: url, options: .alwaysMapped)
                let decoder = JSONDecoder()
                do{
                    let response = try decoder.decode(ResponseObject.self, from: data!)
                    DispatchQueue.main.async {
                        if (response.quizzes!.count != 0){
                            self.totalQuizzes.append(contentsOf: response.quizzes!)
                            self.prepareImages()
                            print(self.totalQuizzes.count)
                        }
                    }
                }
                catch {
                    print(error)
                }
            }
        }
    }
    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        print("num of rows \(totalQuizzes.count)")
        return totalQuizzes.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print("in cellForRowAt \(indexPath.row)")
        let cell = tableView.dequeueReusableCell(withIdentifier: "Quiz")!
        let quiz = totalQuizzes[indexPath.row]
        let images = quizzesImages[indexPath.row]
        cell.textLabel?.text = "\(quiz.author?.username) \(quiz.question)"
        cell.imageView?.image = images
        return cell
    }}

关于ios - 使用 DispatchQueue 索引超出范围异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53450085/

相关文章:

ios - cellForRowAtIndexPath 中对象的潜在泄漏

swift - 旋转时移动 Sprite 节点

objective-c - 提高代码 objective-c 的处理速度

ios - 将 block 并发插入并发队列

ios - 在 Apple 的 Foundation/Swift/Objective-C 中,runLoop.run 如何阻塞,但仍允许 DispatchWorkItems 处理?

python - 从Pygame/PyOpenGL到iOS的最简单路径。

ios - 同时处理两个手势识别器

ios - 在 mac 上安装配置文件时出错。 (未能在此设备上安装一个或多个配置文件)

swift - 用于日志记录的 SQLite 跟踪

Swift:二元运算符 '==' 不能应用于 "protocol"类型的操作数