调用proceedToNextScanPass()时ObjectCaptureSession崩溃求助
ObjectCapture API 调用
beginNewScanPass() 频繁崩溃的解决方案 问题概述
开发基于ObjectCapture API的应用时,调用beginNewScanPass()(封装在自定义方法proceedToNextScanPass()中)时频繁崩溃,10次操作中有9次触发崩溃。已参考官方示例实现,但未定位到问题,且该方法无法抛出错误,调试难度大。
核心解决方案
1. 避免ObjectCaptureSession悬垂引用
ObjectCaptureSession是引用类型,若UI持有已被销毁的session实例,调用方法会直接触发崩溃。
- 修改点:将
CapturePrimaryView中直接传递session参数的方式,改为从环境变量AppDataModel中获取,确保UI始终持有有效实例:struct CapturePrimaryView: View { @Environment(AppDataModel.self) var appModel @State private var currentOrbit = 1 // 从appModel中获取session,而非直接传入 private var session: ObjectCaptureSession? { appModel.objectCaptureSession } var body: some View { ZStack { if let session, session.userCompletedScanPass { if currentOrbit <= 2 { Button("Continue") { proceedToNextScanPass() } .buttonStyle(.borderedProminent) .tint(.yellow) } else if currentOrbit == 3 { Button("Finish") { completeCapture() } .buttonStyle(.borderedProminent) } } else if let session { ObjectCaptureView(session: session, cameraFeedOverlay: { GradientBackground() }) .hideObjectReticle(false) .transition(.opacity) } } .onAppear { UIApplication.shared.isIdleTimerDisabled = true } .onChange(of: session?.state) { _, newValue in print("Session State Changed: \(newValue ?? .notSet)") } .id(session?.id ?? UUID()) } // ... 原有proceedToNextScanPass、completeCapture方法 } - 同步修改ContentView:移除直接传递session的代码:
// ContentView中替换原有CapturePrimaryView的调用 CapturePrimaryView() .overlay { Spacer() if let session = appModel.objectCaptureSession { CaptureButton(session: session, hasDetectionFailed: $hasDetectionFailed) } }
2. 严格校验Session状态与忙碌状态
仅判断session.state == .capturing不足以保证操作安全,需同时校验isBusy属性,避免在session处于过渡状态时调用方法:
@MainActor private func proceedToNextScanPass() { guard let session, session.state == .capturing, !session.isBusy else { print("无法启动新扫描阶段:当前状态无效。状态:\(session?.state ?? .notSet),是否忙碌:\(session?.isBusy ?? true)") return } session.beginNewScanPass() print("新扫描阶段启动成功") }
3. 同步扫描阶段与本地状态
手动维护currentOrbit易导致与session实际扫描阶段不同步,需通过监听session的scanPassIndex自动更新:
struct CapturePrimaryView: View { // ... 原有属性 var body: some View { ZStack { // ... 原有UI逻辑 } .onChange(of: session?.scanPassIndex) { _, newValue in if let newValue { currentOrbit = newValue + 1 // scanPassIndex从0开始计数 } } // ... 其他修饰符 } }
4. 确保所有Session操作在主线程执行
ObjectCaptureSession的所有方法必须在主线程调用,显式标记方法为@MainActor避免线程问题:
@MainActor private func proceedToNextScanPass() { // ... 原有逻辑 }
5. 添加详细日志辅助调试
通过系统日志记录Session状态变化,便于定位崩溃触发时的上下文:
import os.log private let objectCaptureLogger = OSLog(subsystem: "com.your.app.bundleid", category: "ObjectCapture") @MainActor private func proceedToNextScanPass() { guard let session, session.state == .capturing, !session.isBusy else { os_log("启动扫描阶段失败:状态=%{public}@,忙碌=%{public}d", log: objectCaptureLogger, type: .error, "\(session?.state ?? .notSet)", session?.isBusy ?? true) return } os_log("启动第%{public}d次扫描阶段", log: objectCaptureLogger, type: .info, currentOrbit) session.beginNewScanPass() }
额外排查点
- 检查
CaptureFolderManager的目录权限,确保Session能正常读写扫描图片与 checkpoint 文件 - 在XCode中开启Zombie Objects检测,定位是否存在野指针访问
- 测试时使用真机(ObjectCapture仅支持真机),避免模拟器环境差异
内容的提问来源于stack exchange,提问作者phast
相关产品推荐
相关产品推荐

