import AVFoundation /// 扫码采集会话的配置与运行接口,便于独立验证生命周期顺序。 protocol QRCodeCaptureSession: AnyObject { /// 当前是否正在采集。 var isRunning: Bool { get } /// 开始批量配置。 func beginConfiguration() /// 提交批量配置。 func commitConfiguration() /// 启动采集。 func startRunning() /// 停止采集。 func stopRunning() } extension AVCaptureSession: QRCodeCaptureSession {} /// 在同一串行队列中配置和启停相机,避免配置提交与启动交叉执行。 final class QRCodeCaptureSessionController { private let session: QRCodeCaptureSession private let queue: DispatchQueue private var isConfigured = false /// 创建会话生命周期控制器;队列必须为串行队列。 init(session: QRCodeCaptureSession, queue: DispatchQueue = DispatchQueue(label: "com.zhifly.suixinkan.qr-capture")) { self.session = session self.queue = queue } /// 在配置事务内添加输入输出,提交完成后在主线程通知结果。 func configure(_ configuration: @escaping () -> Bool, completion: @escaping (Bool) -> Void) { queue.async { [self] in if !isConfigured { session.beginConfiguration() isConfigured = configuration() session.commitConfiguration() } let succeeded = isConfigured DispatchQueue.main.async { completion(succeeded) } } } /// 按提交顺序更新运行状态,运行状态的读取也限定在会话队列内。 func setRunning(_ shouldRun: Bool) { queue.async { [self] in if shouldRun { guard isConfigured, !session.isRunning else { return } session.startRunning() } else if session.isRunning { session.stopRunning() } } } }