WKWebView 初始化时,有一个参数叫configuration,它是WKWebViewConfiguration类型的参数,而WKWebViewConfiguration有一个属性叫userContentController,它又是WKUserContentController类型的参数。
WKUserContentController对象有一个方法-addScriptMessageHandler:name:
初始化webView
lazy var webView: WKWebView = {
let webCfg: WKWebViewConfiguration = WKWebViewConfiguration()
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
preferences.javaScriptCanOpenWindowsAutomatically = false
var userController: WKUserContentController = WKUserContentController()
/// 注册JS的方法
userController.add(self, name: "requestDeviceChannel")
webCfg.userContentController = userController
webCfg.preferences = preferences
let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height), configuration: webCfg)
webView.tintColor = .clear
return webView
}()
- addScriptMessageHandler:name:有两个参数,第一个参数是userContentController的代理对象,第二个参数是JS里发送postMessage的对象。
当前控制器关闭后,这时我们会发现deinit不会被调用,也就是说控制器对象没有释放。问题就出现在:
1、// addScriptMessageHandler 很容易导致循环引用
2、// 控制器 强引用了WKWebView,WKWebView copy(强引用了)configuration, configuration copy (强引用了)userContentController
3、// userContentController 强引用了 self (控制器)
解决:打破强引用。让userContentController的代理对象变成VKFlowWebBaseVCProxyHandler。
/// 解决【addScriptMessageHandler】引起WKWebView循环引用
let proxy = VKFlowWebBaseVCProxyHandler()
proxy.webVC = self
/// 注册JS的方法
//userController.add(self, name: "requestDeviceChannel")
userController.add(proxy, name: "requestDeviceChannel")
VKFlowWebBaseVCProxyHandler里的webVC用weak修饰。
class VKFlowWebBaseVCProxyHandler: NSObject, WKScriptMessageHandler {
weak var webVC: VKFlowWebBaseViewController?
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
VVLogger.debug(message.body)
VVLogger.debug("VKFlowWebVCProxyHandler: message name: \(message.name)")