WKScriptMessageHandlerWithReply代理方法非主线程调用的原因及解决办法
问题原因
当你用async修饰WKScriptMessageHandlerWithReply的回调方法时,系统会将该方法的执行调度到后台线程(而非默认的主线程)。而WKScriptMessage的所有属性(如name、body)属于WebKit的UI相关对象,苹果明确要求必须在主线程访问,因此在后台线程直接访问这些属性就会触发主线程检查报错。
重构前的代码可能是在WebViewController(UIViewController子类,默认主线程执行)中处理回调,分离到WebController(普通NSObject子类)后,没有强制主线程执行的约束,加上async修饰导致调度到后台,才出现这个问题。
解决方法
方法1:先在主线程提取消息属性,再异步处理逻辑
先在主线程把message的name和body取出来,再执行异步业务逻辑,避免在后台线程直接访问WKScriptMessage对象:
extension WebController: WKScriptMessageHandlerWithReply { func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { // 主线程提取属性 let messageName = DispatchQueue.main.sync { message.name } let messageBody = DispatchQueue.main.sync { message.body } guard messageName == MESSAGE_HANDLER_NAME else { return (nil, "invalid handler") } if messageBody as? String == "some_event_name" { return await performSomeAction() } return (nil, "unknown event") } }
方法2:用MainActor确保属性访问在主线程执行
通过@MainActor指定闭包内代码在主线程运行,安全访问WKScriptMessage属性后再处理异步逻辑:
extension WebController: WKScriptMessageHandlerWithReply { func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { let result = await Task { @MainActor in guard message.name == MESSAGE_HANDLER_NAME else { return (nil, "invalid handler") as (Any?, String?) } if message.body as? String == "some_event_name" { return await self.performSomeAction() } return (nil, "unknown event") }.value return result } }
方法3:给WebController标记MainActor
如果WebController的大部分逻辑都需要和UI交互,可以直接给类标记@MainActor,让所有方法默认在主线程执行:
@MainActor class WebController: NSObject { func performSomeAction() async -> (Any?, String?) { // ... } } extension WebController: WKScriptMessageHandlerWithReply { func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { guard message.name == MESSAGE_HANDLER_NAME else { return (nil, "invalid handler") } if message.body as? String == "some_event_name" { return await performSomeAction() } return (nil, "unknown event") } }
内容的提问来源于stack exchange,提问作者Koen.
相关产品推荐
相关产品推荐

