SwiftUI中为StateObject属性赋值视图函数的警告解决办法
解决SwiftUI中函数赋值给ObservableObject属性时的MainActor警告问题
我在SwiftUI视图中尝试将一个常规函数赋值给用@StateObject修饰的SpeechSynthesizer类的属性,代码如下:
视图代码:
var body: some View { contentView .task { navigation.tabBarVisibility = .hidden viewModel.fetchWordset() synthesizer.onNextUtteranceEnded = onNextUtteranceEnded } } private func onNextUtteranceEnded(_ utterance: AVSpeechUtterance) { guard utterance.voice?.language == "pl-PL" else { return } viewModel.next() }
SpeechSynthesizer类定义:
public final class SpeechSynthesizer: NSObject, ObservableObject, AVSpeechSynthesizerDelegate { @Published public var isSpeaking = false public var onNextUtteranceStarted: ((AVSpeechUtterance) -> Void)? public var onNextUtteranceEnded: ((AVSpeechUtterance) -> Void)? }
此时收到警告:
Converting function value of type '@MainActor (AVSpeechUtterance) -> ()' to '(AVSpeechUtterance) -> Void' loses global actor 'MainActor'
问题原因
SwiftUI视图的所有属性和方法默认运行在@MainActor上下文(主线程)中,而SpeechSynthesizer的闭包属性未标注任何actor,导致将属于@MainActor的函数赋值给它时,丢失了主Actor上下文信息,触发警告。
解决方法
方法1:修改SpeechSynthesizer类,给闭包属性添加@MainActor标注
如果有权限修改SpeechSynthesizer类,直接给闭包属性加上@MainActor,让类型完全匹配:
public final class SpeechSynthesizer: NSObject, ObservableObject, AVSpeechSynthesizerDelegate { @Published public var isSpeaking = false @MainActor public var onNextUtteranceStarted: ((AVSpeechUtterance) -> Void)? @MainActor public var onNextUtteranceEnded: ((AVSpeechUtterance) -> Void)? }
此时视图中默认@MainActor的函数与闭包属性类型一致,警告自动消失。
方法2:不修改SpeechSynthesizer类,显式指定闭包的MainActor上下文
如果无法修改类,可在赋值时将函数包装为显式的@MainActor闭包:
.task { navigation.tabBarVisibility = .hidden viewModel.fetchWordset() synthesizer.onNextUtteranceEnded = { @MainActor utterance in self.onNextUtteranceEnded(utterance) } }
或者在闭包内部通过MainActor.run确保主线程执行:
synthesizer.onNextUtteranceEnded = { utterance in MainActor.run { self.onNextUtteranceEnded(utterance) } }
方法3:在SpeechSynthesizer内部处理主线程切换
由于AVSpeechSynthesizerDelegate的方法可能在后台线程触发,可在类内部代理方法中强制切换到主线程执行闭包:
public final class SpeechSynthesizer: NSObject, ObservableObject, AVSpeechSynthesizerDelegate { @Published public var isSpeaking = false public var onNextUtteranceStarted: ((AVSpeechUtterance) -> Void)? public var onNextUtteranceEnded: ((AVSpeechUtterance) -> Void)? public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) { DispatchQueue.main.async { self.onNextUtteranceEnded?(utterance) } } // 同理处理onNextUtteranceStarted对应的代理方法 }
这样无论闭包是否标注@MainActor,都会在主线程执行,同时消除警告。
内容的提问来源于stack exchange,提问作者Michał Ziobro
相关产品推荐
相关产品推荐

