WKWebView在deinit时崩溃

15

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

我的问题在于WKWebView消息处理程序泄漏,导致子视图控制器无法释放。

经过一些阅读,我找到了一种修复保留循环的方法,使用这个修复方法 - WKWebView causes my view controller to leak

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

有什么想法或方向可能出了问题吗?

谢谢

更新 这是我的代码 - Code Gist

4个回答

12

将此代码放在子视图控制器的deinit方法中:

webView.scrollView.delegate = nil

哇,谢谢!我们在这个问题上碰了壁,你能解释一下你是怎么找到答案的吗? - shannoga
@shannoga 看起来 WKWebView 存储了指向您的委托的 __unsafe_unretained 指针。有时在视图控制器释放后,Web 视图不会立即释放,这会导致 Web 视图尝试通知委托某些内容时崩溃。 - Timur Bernikovich

9

不要忘记移除您添加的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中解决了问题。


感谢您所做的工作。我会检查其他原因。 - shannoga

0

记住,原因可能是对它的弱引用。 我确定你已经使用WKWebView实例化了包装类的本地变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接