global actorを用いて実装していたところ上記のエラーが出た。
調べてみるとこのエラーが出る状況は色々あるようで、その中の一つが以下の例なのでもし参考になればと思い投稿しました。
状況としては以下のように preconcurrencyなAVFoundationやSpeechを用いていたため、actorに隔離する必要があった。
また、値を特定のactorに隔離して監視したい要件があっため global actorを利用することになった。
import AVFoundation
import Speech
@globalActor
public struct SpeechActor {
public actor ActorType {}
public static let shared: ActorType = ActorType()
}
@SpeechActor
public final class SpeechRecognizer {
}
そして、このglobal actor内の以下の部分で毎回クラッシュした
func prepareEngine() throws -> (AVAudioEngine, SFSpeechAudioBufferRecognitionRequest) {
let (audioEngine, request) = (AVAudioEngine(), SFSpeechAudioBufferRecognitionRequest())
request.shouldReportPartialResults = true
request.requiresOnDeviceRecognition = false
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playAndRecord, mode: .measurement, options: .duckOthers)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
let inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, _) in
request.append(buffer)
}
audioEngine.prepare()
try audioEngine.start()
return (audioEngine, request)
}
エラーはIncorrect actor executor assumption
と表示されていた
そもそもここにある、audioEngineやSFSpeechAudioBuffer...などはSendableに準拠できない参照型(reference type)のインスタンスであり、内部で他のactor等が使われていた場合に値が別のactorに隔離されているとその値を取得できないということになりかねない(あくまでも予想)
調べてもよくわからなかったが、global actorに隔離されていることがまずいのであれば nonisolatedで呼んでみれば良いでのはと試したところうまく行った。
つまり
nonisolated static func prepareEngine() throws -> (AVAudioEngine, SFSpeechAudioBufferRecognitionRequest) {
let (audioEngine, request) = (AVAudioEngine(), SFSpeechAudioBufferRecognitionRequest())
request.shouldReportPartialResults = true
request.requiresOnDeviceRecognition = false
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playAndRecord, mode: .measurement, options: .duckOthers)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
let inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, _) in
request.append(buffer)
}
audioEngine.prepare()
try audioEngine.start()
return (audioEngine, request)
}
こうすると上記のクラッシュに合わずに済んだ。
以上