iOS App仅相机拍照后进入ShapeVC滚动崩溃求助
iOS App相机拍照进入ShapeVC后滚动崩溃排查求助
问题描述
- 异常表现:从相册选择图片进入ShapeVC界面运行正常,但从相机拍摄照片进入该界面后,滚动UITableView时崩溃,崩溃前打印的IndexPath为
Section: 7, Row: 0 - 崩溃信息:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x16fa9fff0),崩溃发生在UITableView的Cell复用逻辑行:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HomeAssetsTableCollectionCell", for: indexPath) as? HomeAssetsTableCollectionCell else {
print("Failed to dequeue cell at IndexPath --> Section: (indexPath.section), Row: (indexPath.row)")
return UITableViewCell()
}
- 已完成排查:已正确配置相机和相册权限,使用Leaks和Instruments工具未定位到问题
相关代码实现
UITableView代理方法
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if section == 0 { return 3 } else { return 1 } } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.section == 0 { let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableCollectionCell", for: indexPath) as! HomeTableCollectionCell cell.selectionStyle = .none cell.collectionView.backgroundColor = .clear//UIColor(named: "textFieldBackClr") if let layout = cell.collectionView.collectionViewLayout as? UICollectionViewFlowLayout { layout.scrollDirection = .horizontal } cell.collectionView.accessibilityHint = "\(indexPath.row)" return cell } else { print("Indexpath --> Section: \(indexPath.section), Row: \(indexPath.row)") guard let cell = tableView.dequeueReusableCell(withIdentifier: "HomeAssetsTableCollectionCell", for: indexPath) as? HomeAssetsTableCollectionCell else { print("Failed to dequeue cell at IndexPath --> Section: \(indexPath.section), Row: \(indexPath.row)") return UITableViewCell() } cell.selectionStyle = .none if let layout = cell.collectionView.collectionViewLayout as? UICollectionViewFlowLayout { layout.scrollDirection = .horizontal } cell.btnViewAll.tag = indexPath.section - 1 cell.btnViewAll.addTarget(self, action: #selector(viewAllAction(sender:)), for: .touchUpInside) cell.collectionView.backgroundColor = .clear//UIColor(named: "textFieldBackClr") cell.lblTitleLeftConstraint.constant = isIpad ? 50 : 20 cell.bgView.backgroundColor = .clear//UIColor(named: "textFieldBackClr") print(self.categoryVM.shapes[indexPath.section - 1]) cell.collectionView.accessibilityHint = self.categoryVM.shapes[indexPath.section - 1].key cell.lblTitle.text = self.categoryVM.shapes[indexPath.section - 1].value cell.btnViewAll.isHidden = false cell.viewAllBtnHeight.constant = 29 return cell } } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if indexPath.section == 0{ return isIpad ? 63 : 53 } else { return self.shapesVM.numberOfImages(key: self.categoryVM.shapes[indexPath.section - 1].key) > 0 ? (isIpad ? 294 : 203) : 0 } } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { guard (section == 0) else { return UIView(frame: .zero) } let headerView = Bundle.main.loadNibNamed("TitleHeader", owner: nil, options: nil)?.first as! TitleHeader headerView.titleLbl.text = "Popular Shapes" headerView.titleLbl.font = UIFont.abcWhyte(weight: .bold, size: isIpad ? 24 : 16) headerView.titleLblLetConstraint.constant = isIpad ? 50 : 20 headerView.backgroundColor = .clear//UIColor(named: "textFieldBackClr") return headerView } func numberOfSections(in tableView: UITableView) -> Int { return self.categoryVM.shapes.count + 1} func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { guard (section == 0) else { return 0 } return isIpad ? 107 : 60 } func tableView(_ tableView: UITableView,willDisplay cell: UITableViewCell,forRowAt indexPath: IndexPath) { if let tableViewCell = cell as? HomeTableCollectionCell { print("HomeTableCollectionCell section --> \(indexPath.section)") tableViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow:indexPath.row,forSection: indexPath.section) } else if let tableViewCell = cell as? HomeAssetsTableCollectionCell { print("HomeAssetsTableCollectionCell section --> \(indexPath.section)") let delay = self.shapesVM.numberOfImages(key: self.categoryVM.shapes[indexPath.section - 1].key) > 0 ? 0 : 0.5 tableViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.row, forSection: indexPath.section, withDelay: delay) } } func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { if let tableViewCell = cell as? HomeTableCollectionCell { print("HomeTableCollectionCell section --> \(indexPath.section)") tableViewCell.collectionView.delegate = nil tableViewCell.collectionView.dataSource = nil } else if let tableViewCell = cell as? HomeAssetsTableCollectionCell { print("HomeAssetsTableCollectionCell section --> \(indexPath.section)") tableViewCell.collectionView.delegate = nil tableViewCell.collectionView.dataSource = nil } }
UICollectionView代理方法
func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { if collectionView.tag == 0{ return getRowForPopularKeys(collectionView: collectionView) } return shapesVM.numberOfImages(key: collectionView.accessibilityHint ?? "") } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if collectionView.tag == 0 { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeTextCell", for: indexPath) as! HomeTextCell populateDataForPupularKey(collectionView: collectionView, cell: cell, indexPath: indexPath) return cell } else { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as! ImageCell cell.cellImage.transform = .identity cell.viewMain.backgroundColor = UIColor(hexString: "121212")//UIColor(named: "textFieldBackClr")! let data = shapesVM.itemAtIndex(indexPath.row, key: collectionView.accessibilityHint ?? "") _APP_MANAGER.setImageUsingUrl(avtarImage: cell.cellImage, imageUrl: data.thumbnail_url) cell.proView.isHidden = !shapesVM.itemAtIndex(indexPath.row, key: collectionView.accessibilityHint ?? "").is_premium cell.viewMain.layer.borderColor = UIColor.clear.cgColor//UIColor(hexString: "5B5C61").cgColor cell.cellImage.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) return cell } }
排查线索与解决方案
- 数据源一致性校验
- 崩溃时Section为7,对应
categoryVM.shapes的索引是6,检查相机拍照进入后categoryVM.shapes的数量是否在滚动过程中发生变化,导致indexPath.section -1越界访问 - 验证
heightForRowAt方法中,shapesVM.numberOfImages(key:)传入的key是否有效,相机场景下是否存在key为空或未注册的情况
- 崩溃时Section为7,对应
- Cell复用与内存管理
- 确认
HomeAssetsTableCollectionCell的xib或类是否已正确注册到UITableView,相机进入场景下是否出现注册失效的情况 - 移除
didEndDisplaying中设置collectionView的delegate和dataSource为nil的逻辑,改为在setCollectionViewDataSourceDelegate方法中确保正确绑定,避免出现野指针 - 检查
_APP_MANAGER.setImageUsingUrl方法是否存在异步回调中引用已释放的cell对象,建议改用弱引用持有cell:weak var weakCell = cell _APP_MANAGER.setImageUsingUrl(avtarImage: weakCell?.cellImage ?? UIImageView(), imageUrl: data.thumbnail_url)
- 确认
- 线程安全检查
- 确认相机拍照完成后,
categoryVM.shapes或shapesVM的数据更新操作是否在主线程执行,所有UI相关的操作必须在主线程完成
- 确认相机拍照完成后,
- 添加索引越界防护
- 在
cellForRowAt的else分支中添加索引校验,避免越界访问:guard indexPath.section - 1 < self.categoryVM.shapes.count else { print("Invalid section index: \(indexPath.section), shapes count: \(self.categoryVM.shapes.count)") return UITableViewCell() } - 在
shapesVM.itemAtIndex方法中添加索引越界判断,返回默认数据或空对象,避免因索引无效导致崩溃
- 在
内容的提问来源于stack exchange,提问作者Saurav Darji
相关产品推荐
相关产品推荐

