WKWebView causes my view controller to leak

后端 未结 6 1921
礼貌的吻别
礼貌的吻别 2020-12-12 11:52

My view controller displays a WKWebView. I installed a message handler, a cool Web Kit feature that allows my code to be notified from inside the web page:

o         


        
6条回答
  •  时光说笑
    2020-12-12 12:31

    Basic problem: The WKUserContentController holds a strong reference to all WKScriptMessageHandlers that were added to it. You have to remove them manually.

    Since this is still a problem with Swift 4.2 and iOS 11 I want to suggest a solution which is using a handler which is separate from the view controller that holds the UIWebView. This way the view controller can deinit normally and tell the handler to clean up as well.

    Here is my solution:

    UIViewController:

    import UIKit
    import WebKit
    
    class MyViewController: JavascriptMessageHandlerDelegate {
    
        private let javascriptMessageHandler = JavascriptMessageHandler()
    
        private lazy var webView: WKWebView = WKWebView(frame: .zero, configuration: self.javascriptEventHandler.webViewConfiguration)
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.javascriptMessageHandler.delegate = self
    
            // TODO: Add web view to the own view properly
    
            self.webView.load(URLRequest(url: myUrl))
        }
    
        deinit {
            self.javascriptEventHandler.cleanUp()
        }
    }
    
    // MARK: - JavascriptMessageHandlerDelegate
    extension MyViewController {
        func handleHelloWorldEvent() {
    
        }
    }
    

    Handler:

    import Foundation
    import WebKit
    
    protocol JavascriptMessageHandlerDelegate: class {
        func handleHelloWorld()
    }
    
    enum JavascriptEvent: String, CaseIterable {
        case helloWorld
    }
    
    class JavascriptMessageHandler: NSObject, WKScriptMessageHandler {
    
        weak var delegate: JavascriptMessageHandlerDelegate?
    
        private let contentController = WKUserContentController()
    
        var webViewConfiguration: WKWebViewConfiguration {
            for eventName in JavascriptEvent.allCases {
                self.contentController.add(self, name: eventName.rawValue)
            }
    
            let config = WKWebViewConfiguration()
            config.userContentController = self.contentController
    
            return config
        }
    
        /// Remove all message handlers manually because the WKUserContentController keeps a strong reference on them
        func cleanUp() {
            for eventName in JavascriptEvent.allCases {
                self.contentController.removeScriptMessageHandler(forName: eventName.rawValue)
            }
        }
    
        deinit {
            print("Deinitialized")
        }
    }
    
    // MARK: - WKScriptMessageHandler
    extension JavascriptMessageHandler {
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            // TODO: Handle messages here and call delegate properly
            self.delegate?.handleHelloWorld()
        }
    }
    

提交回复
热议问题