iOS WKURLSchemeHandler调用didReceiveResponse时EXC_BAD_ACCESS崩溃求助
WKURLSchemeHandler调用
urlSchemeTask.didReceive(urlResponse)时偶发EXC_BAD_ACCESS崩溃问题 问题描述
我使用WKURLSchemeHandler处理内部请求(如首页、网页恢复等),但调用urlSchemeTask.didReceive(urlResponse)时偶尔会触发EXC_BAD_ACCESS崩溃。
崩溃调用栈
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0) frame #0: 0x00000001a7dd23a0 JavaScriptCore`WTF::StringImpl::hashSlowCase() const + 92 frame #1: 0x00000001a7d8266c JavaScriptCore`WTF::HashTable<WTF::Packed<WTF::StringImpl*>, WTF::Packed<WTF::StringImpl*>, WTF::IdentityExtractor, WTF::DefaultHash<WTF::Packed<WTF::StringImpl*>>, WTF::HashTraits<WTF::Packed<WTF::StringImpl*>>, WTF::HashTraits<WTF::Packed<WTF::StringImpl*>>>::lookupForWriting(WTF::Packed<WTF::StringImpl*> const&) + 88 frame #2: 0x00000001a7d82594 JavaScriptCore`WTF::HashTable<WTF::Packed<WTF::StringImpl*>, WTF::Packed<WTF::StringImpl*>, WTF::IdentityExtractor, WTF::DefaultHash<WTF::Packed<WTF::StringImpl*>>, WTF::HashTraits<WTF::Packed<WTF::StringImpl*>>, WTF::HashTraits<WTF::Packed<WTF::StringImpl*>>>::expand(WTF::Packed<WTF::StringImpl*>*) + 324 frame #3: 0x00000001a7d835e8 JavaScriptCore`WTF::HashTableAddResult<WTF::HashTableIterator<WTF::Packed<WTF::StringImpl*>, WTF::Packed<WTF::StringImpl*>, WTF::IdentityExtractor, WTF::DefaultHash<WTF::Packed<WTF::StringImpl*>>, WTF::HashTraits<WTF::Packed<WTF::StringImpl*>>, WTF::HashTraits<WTF::Packed<WTF::StringImpl*>>>> WTF::HashTable<WTF::Packed<WTF::StringImpl*>, WTF::Packed<WTF::StringImpl*>, WTF::IdentityExtractor, WTF::DefaultHash<WTF::Packed<WTF::StringImpl*>>, WTF::HashTraits<WTF::Packed<WTF::StringImpl*>>, WTF::HashTraits<WTF::Packed<WTF::StringImpl*>>>::addPassingHashCode<WTF::HashSetTranslatorAdapter<WTF::UCharBufferTranslator>, WTF::HashTranslatorCharBuffer<char16_t> const&, WTF::HashTranslatorCharBuffer<char16_t> const&>(WTF::HashTranslatorCharBuffer<char16_t> const&, WTF::HashTranslatorCharBuffer<char16_t> const&) + 692 frame #4: 0x00000001a7d8034c JavaScriptCore`WTF::AtomStringImpl::add(char16_t const*, unsigned int) + 240 frame #5: 0x00000001a7d83e3c JavaScriptCore`WTF::AtomStringImpl::add(__CFString const*) + 332 frame #6: 0x00000001aa7b5010 WebCore`WebCore::ResourceResponse::platformLazyInit(WebCore::ResourceResponseBase::InitLevel) + 336 frame #7: 0x00000001a9f020ec WebKit`void WebCore::ResourceResponseBase::encode<IPC::Encoder>(IPC::Encoder&) const + 124 frame #8: 0x00000001aa250a58 WebKit`bool WebKit::AuxiliaryProcessProxy::send<Messages::WebPage::URLSchemeTaskDidReceiveResponse>(Messages::WebPage::URLSchemeTaskDidReceiveResponse&&, unsigned long long, WTF::OptionSet<IPC::SendOption>) + 268 frame #9: 0x00000001aa22c380 WebKit`WebKit::WebURLSchemeTask::didReceiveResponse(WebCore::ResourceResponse const&) + 192 frame #10: 0x00000001aa106554 WebKit`WTF::Detail::CallableWrapper<-[WKURLSchemeTaskImpl didReceiveResponse:]::$_1, WebKit::WebURLSchemeTask::ExceptionType>::call() + 52 frame #11: 0x00000001aa0fa7e4 WebKit`-[WKURLSchemeTaskImpl didReceiveResponse:] + 100 * frame #12: 0x0000000104dab348 Browser`InternalSchemeHandler.webView(webView=0x000000010c853600, urlSchemeTask=__C.WKURLSchemeTask @ 0x000000016dd7c818, self=0x00000002808efe70) at InternalSchemeHandler.swift:98:27 frame #13: 0x0000000104dabc64 Browser`@objc InternalSchemeHandler.webView(_:start:) at <compiler-generated>:0 frame #14: 0x00000001aa1b8648 WebKit`WebKit::WebURLSchemeHandlerCocoa::platformStartTask(WebKit::WebPageProxy&, WebKit::WebURLSchemeTask&) + 524 frame #15: 0x00000001aa20a0dc WebKit`WebKit::WebURLSchemeHandler::startTask(WebKit::WebPageProxy&, WebKit::WebProcessProxy&, WTF::ObjectIdentifier<WebCore::PageIdentifierType>, WebKit::URLSchemeTaskParameters&&, WTF::CompletionHandler<void (WebCore::ResourceResponse const&, WebCore::ResourceError const&, WTF::Vector<char, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&) + 968 frame #16: 0x00000001aa209cd0 WebKit`WebKit::WebPageProxy::startURLSchemeTaskShared(WTF::Ref<WebKit::WebProcessProxy, WTF::DumbPtrTraits<WebKit::WebProcessProxy>>&&, WTF::ObjectIdentifier<WebCore::PageIdentifierType>, WebKit::URLSchemeTaskParameters&&) + 108 frame #17: 0x00000001aa209c24 WebKit`WebKit::WebPageProxy::startURLSchemeTask(WebKit::URLSchemeTaskParameters&&) + 52 frame #18: 0x00000001aa4d44a8 WebKit`WebKit::WebPageProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 84824 frame #19: 0x00000001a9ef885c WebKit`IPC::MessageReceiverMap::dispatchMessage(IPC::Connection&, IPC::Decoder&) + 124 frame #20: 0x00000001aa2272f0 WebKit`WebKit::WebProcessProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 40 frame #21: 0x00000001a9edbdec WebKit`IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder>>) + 656 frame #22: 0x00000001a9edb6f4 WebKit`IPC::Connection::dispatchIncomingMessages() + 460 frame #23: 0x00000001a7dbe3c8 JavaScriptCore`WTF::RunLoop::performWork() + 484 frame #24: 0x00000001a7dbf0b0 JavaScriptCore`WTF::RunLoop::performWork(void*) + 36 frame #25: 0x000000019db7076c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 frame #26: 0x000000019db70668 CoreFoundation`__CFRunLoopDoSource0 + 208 frame #27: 0x000000019db6f960 CoreFoundation`__CFRunLoopDoSources0 + 268 frame #28: 0x000000019db69a8c CoreFoundation`__CFRunLoopRun + 824 frame #29: 0x000000019db6921c CoreFoundation`CFRunLoopRunSpecific + 600 frame #30: 0x00000001b4be8784 GraphicsServices`GSEventRunModal + 164 frame #31: 0x00000001a05a2200 UIKitCore`-[UIApplication _run] + 1072 frame #32: 0x00000001a05a7a74 UIKitCore`UIApplicationMain + 168 frame #33: 0x000000010221d568 TDMBrowser`main at AppDelegate.swift:32:7 frame #34: 0x000000019d8296c0 libdyld.dylib`start + 4
我的URLSchemeHandler实现代码
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { if taskDic[ObjectIdentifier(urlSchemeTask)] != false { taskDic[ObjectIdentifier(urlSchemeTask)] = true } guard let url = urlSchemeTask.request.url else { if taskDic[ObjectIdentifier(urlSchemeTask)] == true { urlSchemeTask.didFailWithError(InternalPageSchemeHandlerError.badURL) taskDic.removeValue(forKey: ObjectIdentifier(urlSchemeTask)) } return } let path = url.path.starts(with: "/") ? String(url.path.dropFirst()) : url.path guard let responder = InternalSchemeHandler.responders[path] else { if taskDic[ObjectIdentifier(urlSchemeTask)] == true { urlSchemeTask.didFailWithError(InternalPageSchemeHandlerError.noResponder) taskDic.removeValue(forKey: ObjectIdentifier(urlSchemeTask)) } return } guard let (urlResponse, data) = responder.response(forRequest: urlSchemeTask.request) else { if taskDic[ObjectIdentifier(urlSchemeTask)] == true { urlSchemeTask.didFailWithError(InternalPageSchemeHandlerError.responderUnableToHandle) taskDic.removeValue(forKey: ObjectIdentifier(urlSchemeTask)) } return } if taskDic[ObjectIdentifier(urlSchemeTask)] == true { urlSchemeTask.didReceive(urlResponse) urlSchemeTask.didReceive(data) urlSchemeTask.didFinish() taskDic.removeValue(forKey: ObjectIdentifier(urlSchemeTask)) } } func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) { moduleLogger.debug("stop urlSchemeTask: \(urlSchemeTask)") taskDic[ObjectIdentifier(urlSchemeTask)] = false }
解决建议
根据调用栈分析,崩溃发生在WebCore处理ResourceResponse的platformLazyInit阶段,涉及StringImpl的哈希计算,大概率是ResourceResponse内部的某个字符串属性为空或已被释放,或多线程访问导致的野指针问题。
- 检查
responder.response(forRequest:)返回的urlResponse初始化是否完整:确保URL、MIME类型等必填参数不为空,HTTP头中避免传入nil或空字符串。比如创建URLResponse时,必须传入有效的URL对象,MIME类型如text/html不能留空。 - 强制主线程执行task回调:即使当前调用在主线程,若
responder.response(forRequest:)在后台线程生成urlResponse,可能存在线程安全问题。所有urlSchemeTask的didReceive、didFinish等方法调用,都要切换到主线程执行:DispatchQueue.main.async { urlSchemeTask.didReceive(urlResponse) urlSchemeTask.didReceive(data) urlSchemeTask.didFinish() } - 简化taskDic的管理逻辑:原逻辑存在初始化不严谨的问题,可改用
defer统一清理,避免重复判断:func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { let taskID = ObjectIdentifier(urlSchemeTask) taskDic[taskID] = true defer { taskDic.removeValue(forKey: taskID) } guard taskDic[taskID] == true, let url = urlSchemeTask.request.url else { urlSchemeTask.didFailWithError(InternalPageSchemeHandlerError.badURL) return } // 后续逻辑中,每次执行回调前都先判断任务是否已取消 guard taskDic[taskID] == true else { return } // ... 其余代码 } - 确保task回调只调用一次:避免因任务取消后仍执行
didReceive等方法,导致WebKit内部状态混乱。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

