从“cellForRowAtIndexPath”

时间:2015-08-28 20:03:47

标签: ios uitableview

我花了好几个小时搞清楚这一点。最后,我不得不创建一个小型测试项目来隔离问题。简而言之,当我滚动浏览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成员可能会有所帮助。

1 个答案:

答案 0 :(得分:2)

我将扩展David H所说的内容。

这不是错误。 iOS需要使用在填充期间不可变的数据来填充表。它已经调用了表中应该有多少行,每个单元应该有多高,有多少部分等等,因为它在呈现UITableViewCell之前预先计算了某些东西。

如果在当前线程上更改此项,则所有这些计算都将不匹配,从而导致错误。在某些特定情况下,它可能不会抛出错误(如果数据的更改方式可能会导致单元格的数量相同,高度相同等),但它肯定会因为您的错误而发生错误。做。

异步调用意味着数据仅在UITableViewDelegate / UITableViewDataSource的眼中发生变化,之后它已完成所有必要的计算以填充UITableView。