我花了好几个小时搞清楚这一点。最后,我不得不创建一个小型测试项目来隔离问题。简而言之,当我滚动浏览UITableView
时,我发生了一个奇怪的崩溃,“随机”发生。崩溃只发生在我快速滚动并且将行插入UITableView
时。 (当用户接近表视图的末尾时,我批量处理新数据)。崩溃发生在UIKit
框架内:
Fatal Exception: NSRangeException
*** -[__NSArrayM objectAtIndex:]: index 15 beyond bounds [0 .. 14]
raw
0 CoreFoundation __exceptionPreprocess
2 CoreFoundation -[__NSArrayM removeObjectAtIndex:]
3 UIKit -[_UITableViewUpdateSupport(Private) _setupAnimationsForExistingVisibleCells]
4 UIKit -[_UITableViewUpdateSupport _setupAnimations]
5 UIKit -[UITableView _updateWithItems:updateSupport:]
6 UIKit -[UITableView _endCellAnimationsWithContext:]
我的数据源和tableview对齐完美(这不是我的书本保持错误)。所以我创建了一个测试项目并复制了错误。我发现如果我直接在cellForRowAtIndexPath
内插入行,则会发生崩溃:
import UIKit
let batchSize = 30
let cellID = "CellIdentifier"
class ViewController: UIViewController {
var datasource: [Int] = Array(0..<batchSize)
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.setTranslatesAutoresizingMaskIntoConstraints(false)
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellID)
view.addSubview(tableView)
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[v]|", options: nil, metrics: nil, views: [ "v" : tableView ]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[v]|", options: nil, metrics: nil, views: [ "v" : tableView ]))
}
private func insertNewBatch() {
let newData = Array(datasource.count..<datasource.count + batchSize)
datasource += newData
tableView.insertRowsAtIndexPaths(newData.map{ NSIndexPath(forRow: $0, inSection: 0) }, withRowAnimation: .None)
}
}
extension ViewController : UITableViewDataSource {
func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 }
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return datasource.count }
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellID, forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = "Cell \(datasource[indexPath.row])"
if datasource.count - indexPath.row < 10 {
// dispatch_async(dispatch_get_main_queue()) {
self.insertNewBatch()
// }
}
return cell
}
}
但是,如果<{em> I dispath_async
主线程上的方法调用(基本上在返回单元格后放置调用),则不会发生崩溃。
在我的主项目中,批处理加载过程可以同步返回,因此基本上来自cellForRow...
委托方法...通过几个框架的albiet。大部分时间它是异步的,外观是随机的。
我的问题是:这是我应该报告的错误吗?我不知道UITableView
禁止在委托方法中修改表的任何地方,但我想确保在我提交之前这不是已知/预期的行为。另外,我认为如果他们遇到这个问题,对其他SO成员可能会有所帮助。
答案 0 :(得分:2)
我将扩展David H所说的内容。
这不是错误。 iOS需要使用在填充期间不可变的数据来填充表。它已经调用了表中应该有多少行,每个单元应该有多高,有多少部分等等,因为它在呈现UITableViewCell之前预先计算了某些东西。
如果在当前线程上更改此项,则所有这些计算都将不匹配,从而导致错误。在某些特定情况下,它可能不会抛出错误(如果数据的更改方式可能会导致单元格的数量相同,高度相同等),但它肯定会因为您的错误而发生错误。做。
异步调用意味着数据仅在UITableViewDelegate / UITableViewDataSource的眼中发生变化,之后它已完成所有必要的计算以填充UITableView。