TableView null值?致命错误:索引超出范围

时间:2018-02-08 23:57:27

标签: swift

我正在制作一个从数据库中读取消息的程序。

程序每10秒将数据下载到一个数组中。

但有时会出现问题..有时我的应用随机会抛出错误“致命错误:索引超出范围”

这是(我认为)因为有时URLSession会给myMessages.messages返回null值,或者我不知道:

 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return myMessages.messages.count

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

        let cell = tableView.dequeueReusableCell(withIdentifier: "myOwnCell", for: indexPath) as! MyOwnCell

        // Sometimes here occours a bug:indexpath.row and myMessages.messages.count are equal to 0
        let message = myMessages.messages[indexPath.row]
 }
 ...

我试了一下:

打印出myMessages.messages,myMessages.messages.count和indexPath

在正常情况下,结果如下(带有2条消息):

[<App.BubbleMessage: 0x1c066bc40>, <App.BubbleMessage: 0x1c0678e40>] //messages
2 //messages count
[0, 0] //indexPath
[<App.BubbleMessage: 0x1c066bc40>, <App.BubbleMessage: 0x1c0678e40>]
2
[0, 1]

然后有时会出现错误:

[]
0
[0, 0]

这会产生错误。有一些工作吗?:C

更新

如果我手动删除myMessages.messages中的所有邮件,那么它仍在运行

[] //messages
0 //messages count

并且不会调用cellForRowAt函数..所以我不知道

为什么会这样,如果我手动创建一个空数组,那么它就不会调用cellForRowAt函数,

但有时即使数组仍为空,它也会调用它并抛出错误。为什么呢?

有没有办法防止它发生?

if (myMessages.messages.count == 0){ return }

更新2 :有可能

在数组中加载了2条消息(msg1,msg2)

现在cellForRowAt函数生成第一个函数:msg1

但是当它生成msg1时,从服务器加载的数据仅以某种方式包含1条消息 - &gt;因此,myMessagges.messages将被删除所有()并仅使用1个值进行更新。

所以下次当cellForRowAt在rowNumber 2中查找msg2时,它找不到它。

我该如何解决?

1 个答案:

答案 0 :(得分:0)

您在DispatchQueue.main.async回调中使用URLSession吗?

URLSession使用后台线程来调用API,而UI必须位于主线程(调度队列)上。

第二个问题,如果你真的使用DispatchQueue.main.async,可能会有另一个问题是线程竞赛。

想象一下以下场景

// 1
myMessages.messages.append(contentsOf: [message1, message2])

DispatchQueue.main.async {
    // 2
    tableView.insertRows( /* put here code for insertion of two rows */)
}

// 3
myMessages.messages.append(message3)

DispatchQueue.main.async {
    // 4
    tableView.insertRows( /* put here code for insertion of one rows */)
}

会发生什么?

1 is executed
then 3
then 2 then 4

这意味着

- append to the backend array 2 object
- then append to the backend array 1 object
(now the array has 3 objects inserted, not 2)
- then inform the tableview that 2 objects are inserted
- then inform the tableview that another object is inserted

这会使表视图崩溃,当他被告知只添加2个对象时后端(当他调用numberOfRowsInSection时)得到一个不同的数字{3 inserted}

解决方案是使用一点不变性。获取数组的快照并将其传递给调度队列,成为tableview的新后端。

// 1
myMessages.messages.append(contentsOf: [message1, message2])

let snapshot = myMessages.messages

DispatchQueue.main.async {
    // 2
    self.backendArray = snapshot // Just take the snapshot that is
                                 // captured from the out of the closure
    tableView.insertRows( /* put here code for insertion of two rows */)
}

// 3
myMessages.messages.append(message3)

// Take another snapshot, must be different from the first
let snapshot2 = myMessages.messages

DispatchQueue.main.async {
    // 4
    self.backendArray = snapshot2 // Just take the second snapshot that is
                                  // captured from the out of the
    tableView.insertRows( /* put here code for insertion of one rows */)
}

使用backendArray作为后端(在numberOfRows和cellForRowAt中)而不是真实对象。

现在每次调用dispatch main时都会获取数组的快照,因此即使计算/获取仍然有更多,您的tableview数据源也将与UI一致