You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
        }
    }

排查线索与解决方案

  1. 数据源一致性校验
    • 崩溃时Section为7,对应categoryVM.shapes的索引是6,检查相机拍照进入后categoryVM.shapes的数量是否在滚动过程中发生变化,导致indexPath.section -1越界访问
    • 验证heightForRowAt方法中,shapesVM.numberOfImages(key:)传入的key是否有效,相机场景下是否存在key为空或未注册的情况
  2. 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)
      
  3. 线程安全检查
    • 确认相机拍照完成后,categoryVM.shapes或shapesVM的数据更新操作是否在主线程执行,所有UI相关的操作必须在主线程完成
  4. 添加索引越界防护
    • 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 00:01:00