经常调用" tableView.reloadData"在GCD主要威胁得到崩溃错误"指数超出范围"

时间:2017-08-23 00:05:28

标签: ios swift uitableview asynchronous reloaddata

对不起,我是初学者学习IOS 我的tableView和重装数据有问题 当我经常打电话给#34; getData"时,我会崩溃并得到错误 但我不知道我的数据在哪里崩溃 我想我先调用reloadData,然后list.count已经在全局线程中更改了 有什么建议可以避免吗?
谢谢。

崩溃日志:

fatal error: Index out of range

型号:

class ChatroomList:Model {

    var all:[Chatroom] {
    var rooms:[Chatroom] = [Chatroom]()
    self.chatrooms.forEach({ (id,chatroom) in
        if showType.contains(chatroom.type) {
            rooms.append(chatroom)
        }
    })
    return rooms
    }
}

的ViewController:

import RxCocoa
import RxSwift
import Alamofire

class ListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {                     

    let chatrooms:ChatroomList = ChatroomList()
    var list:[Chatroom] = [Chatroom]()
    var subscribe:Disposable?

    override func viewDidLoad() {
    super.viewDidLoad()

    self.tableView.dataSource = self
    self.tableView.delegate = self

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        subscribe = rooms.notifySubject.subscribe({ json in
                self.getData() //here is called frequently 
        })
        self.getData()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        subscribe?.dispose()
    }

    func getData() {

        var idList:[String] = []

        self.list.removeAll()
        self.list = chatrooms.all

        guard self.list.isEmpty == false else {
            return
        }

        DispatchQueue.global().async() {

            self.list.sort(by: { (a,b) in
                if a.message.datetime.isEmpty {
                    return false
                }

            return a.message.datetime > b.message.datetime
            })

            self.list = self.list.filter { (chatroom) -> Bool in
                if chatroom.id.isEmpty {
                    return true
                }
                if idList.contains(chatroom.id) {
                    return false
                }
                idList.append(chatroom.id)
                return true
            }   

            DispatchQueue.main.sync() {

                self.tableView.reloadData()
        }
      }
    }
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return list.count
}

   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    if list[indexPath.row].type == .city {

        let cell: ChatroomCityTableViewCell = ChatroomCityTableViewCell(style: .default, reuseIdentifier: nil)

        cell.loadByCityChatroom(chatroom: list[indexPath.row], cityId: list[indexPath.row].cityId)

        return cell
    }else{

        let cell: ChatroomTableViewCell = ChatroomTableViewCell(style: .default, reuseIdentifier: nil)
        cell.loadByChatroom(chatroom: list[indexPath.row])

        return cell
    }
}

1 个答案:

答案 0 :(得分:1)

问题很可能是由于您目前如何使用GCD(Grand Central dispatch)造成的。

重新加载时,tableView会询问许多不同的问题,例如每行的行数和单元格。如果其中一个调用之间的数据发生更改,则会导致不一致异常,因为它尝试添加或删除不再表示数据的行数。

在getData函数可以在任何给定时间更改列表时,在主线程上异步重新加载tableView将导致上述错误。

解决方案并不简单,您需要重新考虑如何更新列表,以便在tableView重新加载数据时不会更改。

你可以尝试做的事情是:

func getData() {
    // You cannot clear or change self.list here

    guard !chatrooms.all.isEmpty else { return }

    DispatchQueue.global().async() {
         let updatedData = process(newData: self.chatrooms.all)

         DispatchQueue.main.sync() {
             self.list = updatedData
             self.tableView.reloadData()
         }
    }
}

private func process(newData data: [Chatroom]) -> [Chatroom] {
    // Do all your logic without making any changes to self.list
}

关键是永远不要对重新加载tableView时使用的数据进行任何更改,除非在重新加载之前在主线程juste上同步。