WKWebView 在 deinit 上崩溃

Posted

技术标签:

【中文标题】WKWebView 在 deinit 上崩溃【英文标题】:WKWebView crashes on deinit 【发布时间】:2016-06-02 10:18:36 【问题描述】:

我有这个层次结构 - UIViewController -> ChildUIViewController -> WKWebView。

我遇到了 WKWebView 消息处理程序的问题,该消息处理程序泄漏并阻止子视图控制器被释放。

经过一番阅读,我找到了一种通过使用此修复程序来修复保留周期的方法 - WKWebView causes my view controller to leak

现在我可以看到子视图控制器正在到达 deinit,但之后 WKWebView 在 deinit 上崩溃(Xcode 中没有有用的日志)。

任何想法或方向可能是什么问题?

谢谢

更新 这是我的代码 - Code Gist

【问题讨论】:

【参考方案1】:

把这个放在子视图控制器的deinit方法中:

webView.scrollView.delegate = nil

【讨论】:

哇。谢谢!我们撞到了这个,你能解释一下是怎么发现的吗? @shannoga 看起来像 WKWebView 存储 __unsafe_unretained 指向您的代表的指针。有时在视图控制器释放后不会立即释放 Web 视图,这会在 Web 视图尝试通知委托时导致崩溃。【参考方案2】:

别忘了删除你添加的 WKWebView 的委托:

deinit 
    webView.navigationDelegate = nil
    webView.scrollView.delegate = nil

看起来 WKWebView 存储了指向您的委托的 __unsafe_unretained 指针。有时当 web 视图在视图控制器释放后没有立即释放。当 Web 视图尝试通知委托某些内容时,这会导致崩溃。

【讨论】:

【参考方案3】:

我尝试了与您提到的相同的方法。它非常适合我。我试过的代码是,

class CustomWKWebView : WKWebView 

    deinit 
        print("CustomWKWebView - dealloc")
    




class LeakAvoider : NSObject, WKScriptMessageHandler 
    weak var delegate : WKScriptMessageHandler?
    init(delegate:WKScriptMessageHandler) 
        self.delegate = delegate
        super.init()
    
    func userContentController(userContentController: WKUserContentController,
        didReceiveScriptMessage message: WKScriptMessage) 
            self.delegate?.userContentController(
                userContentController, didReceiveScriptMessage: message)
    

    deinit 
        print("LeakAvoider - dealloc")
    



class ChildViewController: UIViewController , WKScriptMessageHandler

    var webView = CustomWKWebView()

    override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()
    

    override func viewDidLoad() 
        super.viewDidLoad()
        view.addSubview(webView)
        webView.frame = self.view.bounds;
    

    override func viewDidAppear(animated: Bool) 
        super.viewDidAppear(animated)
        let url = NSURL(string: "https://appleid.apple.com")
        webView.loadRequest(NSURLRequest(URL:url!))
        webView.configuration.userContentController.addScriptMessageHandler(
            LeakAvoider(delegate: self), name: "dummy")
    

    func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage)
    

    

    deinit 
        print("ChaildViewController- dealloc")
        webView.stopLoading()
        webView.configuration.userContentController.removeScriptMessageHandlerForName("dummy")
    



class ViewController: UIViewController 

    override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()
    

    override func viewDidLoad() 
        super.viewDidLoad()
    

    deinit 
        print("ViewController - dealloc")
    

弹出ViewController后的日志是:

ViewController - dealloc
ChaildViewController- dealloc
LeakAvoider - dealloc
CustomWKWebView - dealloc

更新

在您的 WebViewViewController 的 viewWillDisappear 函数中添加以下行。

    webView.navigationDelegate = nil
    webView.scrollView.delegate = nil

我尝试在我的代码中设置这两个委托,但它开始崩溃。将以上几行放在 ChildViewController 的 viewWillDisappear 中即可解决。

【讨论】:

感谢您所做的工作。我会检查其他原因。【参考方案4】:

请记住,原因可能是对它的弱引用造成的。 我确定你已经用 WKWebView 实例化了包装类的局部变量。

【讨论】:

以上是关于WKWebView 在 deinit 上崩溃的主要内容,如果未能解决你的问题,请参考以下文章

iOS WKWebView javascript警报在ipad上崩溃

在 iOS14 上释放 wkwebview 时,发送手势事件导致崩溃

为啥即使在 WKWebView 中设置 cookie 后 SSO 也不起作用?

WKWebView崩溃iOS9

WKWebview:从菜单中删除复制、查找、共享按钮并显示自定义

如何让 WKWebView 像 SFSafariViewController 一样全屏显示?