3.E 修复:换用 iOS 17+ AVAudioApplication.requestRecordPermission 避免 EXC_BREAKPOINT

## 问题

第一次调用 prepareaudio 弹出系统权限对话框,用户点允许/拒绝都触发
Thread 10 EXC_BREAKPOINT 崩溃。

## 根因猜测

deprecated 的 AVAudioSession.requestRecordPermission(_:) 在
iOS 26.5 SDK 上内部桥接到 AVAudioApplication 时,与
withCheckedContinuation 的 Swift Concurrency 状态机交互异常。
具体表现:completion 在 Thread 10 触发 cont.resume(returning:),
runtime 检测到某种 actor / continuation 状态违规,触发 trap。

## 修复

新增 `requestMicrophonePermission()` 私有助手:

```swift
if #available(iOS 17.0, *) {
    return await AVAudioApplication.requestRecordPermission()  // 原生 async
} else {
    return await withCheckedContinuation { ... }              // 旧 SDK 兼容
}
```

iOS 17+ 走原生 async API,不经 Continuation 状态机,避开 corner case;
iOS 15.6 / 16 走 deprecated API 兼容路径。

## 验证

BuildProject 通过 0 错误。真机首次启动 → 按录音按钮 → 系统权限对话框
出现 → 点允许/拒绝 → 应无崩溃。
This commit is contained in:
joywayer
2026-06-24 02:05:25 +08:00
parent 6f10d231ad
commit 7d89a9d3ee
@@ -101,11 +101,13 @@ public enum RemoteAudioHandler {
// 首次启动:弹系统权限对话框,**不**启动录音浮层(避免与 touchObserver // 首次启动:弹系统权限对话框,**不**启动录音浮层(避免与 touchObserver
// 死锁——touchObserver 会捕获用户松手点"允许"的动作立刻 stop 录音)。 // 死锁——touchObserver 会捕获用户松手点"允许"的动作立刻 stop 录音)。
// 用户做完选择 / 第二次按录音按钮时走 .authorized 分支正常录音。 // 用户做完选择 / 第二次按录音按钮时走 .authorized 分支正常录音。
_ = await withCheckedContinuation { cont in //
AVAudioSession.sharedInstance().requestRecordPermission { granted in // 优先用 iOS 17+ 的 AVAudioApplication.requestRecordPermission()
cont.resume(returning: granted) // (原生 async,Swift Concurrency 一等公民);iOS 26+ 上
} // deprecated 的 AVAudioSession.requestRecordPermission(_:) +
} // withCheckedContinuation 桥接路径实测会触发 Thread 10
// EXC_BREAKPOINT(疑似系统内部桥接新 API 时状态机异常)。
_ = await requestMicrophonePermission()
// ⚠️ 必须触发 cb(对齐 daoqi ifauth==1 路径行为,daoqi NewRootVC.m:322 // ⚠️ 必须触发 cb(对齐 daoqi ifauth==1 路径行为,daoqi NewRootVC.m:322
// 在 [recorderVC beginRecordByFileName:] 后总是 responseCallback)。 // 在 [recorderVC beginRecordByFileName:] 后总是 responseCallback)。
// //
@@ -336,6 +338,29 @@ public enum RemoteAudioHandler {
callback?(.string("Response from mediaTypeAudio")) callback?(.string("Response from mediaTypeAudio"))
} }
/// 麦克风权限请求(弹系统对话框)。
///
/// iOS 17+ 用新 `AVAudioApplication.requestRecordPermission()`(原生 async);
/// iOS 15.6 / 16 用 deprecated 的 `AVAudioSession.requestRecordPermission(_:)` +
/// withCheckedContinuation 桥接。
///
/// **不用** deprecated API 走新 SDK 路径的原因:实测 iOS 26.5 SDK 上 deprecated
/// API 内部桥接到 AVAudioApplication 时与 withCheckedContinuation 的状态机交互
/// 异常,会在 Thread 10 触发 EXC_BREAKPOINT(疑似系统 bug 或 Swift Concurrency
/// 与 ObjC 桥接的 corner case)。新 API 直接 await 不经 Continuation 状态机,
/// 行为稳定。
private static func requestMicrophonePermission() async -> Bool {
if #available(iOS 17.0, *) {
return await AVAudioApplication.requestRecordPermission()
} else {
return await withCheckedContinuation { (cont: CheckedContinuation<Bool, Never>) in
AVAudioSession.sharedInstance().requestRecordPermission { granted in
cont.resume(returning: granted)
}
}
}
}
@MainActor @MainActor
private static func showMicrophonePermissionAlert(on hostVC: UIViewController?) { private static func showMicrophonePermissionAlert(on hostVC: UIViewController?) {
// 用户已拒绝麦克风权限时调用。iOS 不允许再次主动弹系统权限对话框 // 用户已拒绝麦克风权限时调用。iOS 不允许再次主动弹系统权限对话框