Xcode14.3+Swift5下UITableView分区断言失败问题求助
解决方案
核心问题分析
出现断言错误的本质是UITableView接收到的分区数量与NSFetchedResultsController(以下简称FRC)返回的分区数不匹配,或是sectionNameKeyPath对应的storage.location存在nil值导致分区计算异常;而保存失败则大概率和FRC的排序规则、上下文同步逻辑有关。
具体修复步骤
1. 强制FRC排序规则包含分区字段
FRC要求排序必须优先按sectionNameKeyPath对应的字段排序,否则会出现数据混乱或断言错误。修改FRC初始化代码:
// 先按存储位置排序,再按零件自身属性排序(替换成你的零件实体属性) let sortDescriptors = [ NSSortDescriptor(key: "storage.location", ascending: true), NSSortDescriptor(key: "partName", ascending: true) ] let fetchRequest: NSFetchRequest<PieceEntity> = PieceEntity.fetchRequest() fetchRequest.sortDescriptors = sortDescriptors // 初始化FRC,注意cacheName设为nil避免旧缓存干扰 let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: yourContext, sectionNameKeyPath: "storage.location", cacheName: nil)
2. 处理storage.location为nil的异常数据
如果存在未关联存储位置的零件(storage为nil或storage.location为nil),FRC会将这些数据归为空字符串分区,导致UITableView计算异常。可以二选一处理:
- 过滤无效数据,只查询关联有效存储位置的零件:
fetchRequest.predicate = NSPredicate(format: "storage != nil AND storage.location != nil")
- 保留无效数据,在UITableView代理中给空分区设置默认名称:
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { guard let sectionInfo = frc.sections?[section] else { return nil } return sectionInfo.name.isEmpty ? "未指定存储位置" : sectionInfo.name }
3. 修复保存操作的上下文同步问题
重新设置sectionNameKeyPath后无法保存,需检查以下两点:
- 确保保存操作和FRC使用同一个
NSManagedObjectContext:
do { try yourContext.save() } catch { print("保存失败:\(error.localizedDescription)") }
- 保存成功后手动刷新FRC和UITableView(避免在FRC代理方法中调用,防止循环):
do { try frc.performFetch() tableView.reloadData() } catch { print("重新获取数据失败:\(error.localizedDescription)") }
4. 严格基于FRC实现UITableView数据源方法
禁止硬编码分区数或行数,确保数据源完全依赖FRC返回的数据:
func numberOfSections(in tableView: UITableView) -> Int { return frc.sections?.count ?? 0 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { guard let sectionInfo = frc.sections?[section] else { return 0 } return sectionInfo.numberOfObjects } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID", for: indexPath) let piece = frc.object(at: indexPath) // 配置cell内容 cell.textLabel?.text = piece.partName cell.detailTextLabel?.text = piece.storage?.location return cell }
5. 清除FRC旧缓存(如果曾经设置过cacheName)
旧缓存会导致分区数据异常,初始化前清除对应缓存:
NSFetchedResultsController.deleteCache(withName: "YourOldCacheName")
内容的提问来源于stack exchange,提问作者lauk75
相关产品推荐
相关产品推荐

