I have this hierarchy - UIViewController -> ChildUIViewController -> WKWebView.
I had an issue with the WKWebView message handler that leaked and prevented the child view controller from being released.
After some reading I found a way to fix the retain cycle by using this fix - WKWebView causes my view controller to leak
Now I can see that the child view controller is reaching deinit
but right after that the WKWebView is crashing on deinit
(No useful log from Xcode).
Any Idea or direction what could be the issue ?
Thanks
UPDATE here is my code - Code Gist
Put this inside deinit method of child view controller:
webView.scrollView.delegate = nil
Don't forget to remove WKWebView's delegates you added:
deinit {
webView.navigationDelegate = nil
webView.scrollView.delegate = nil
}
Looks like WKWebView stores __unsafe_unretained pointer to your delegate. Sometimes when web view deallocated not immediate after view controller deallocation. This cause crash when web view tries to notify delegate something.
I tried with same way as you mentioned. It works perfectly for me. Code which i tried is,
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")
}
}
Log after popping ViewController is:
ViewController - dealloc
ChaildViewController- dealloc
LeakAvoider - dealloc
CustomWKWebView - dealloc
UPDATE
Put below lines in your WebViewViewController's viewWillDisappear function.
webView.navigationDelegate = nil
webView.scrollView.delegate = nil
I tried by setting these two delegates in my code and it started crashing. Solved by putting above lines in viewWillDisappear of ChildViewController.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With