概要
WKUserContentController の add(_ scriptMessageHandler:, name: ) メソッドは scriptMessageHandler を強参照するので気をつけよう と言う話です。
WKWebView で JS のメソッドをフックしたい時は下記のように「WKUserContentController にhoge
メソッドが実行されたら self
に教えてね」と言うコードを書きます。
final class WebViewController: UIViewController {
private let webView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
webView.configuration.userContentController.add(self, name: "hoge")
}
}
しかしのこのコードは循環参照を起こします。
原因
WKUserContentController の add メソッドですが、実は 第一引数を強参照します。
もしこの第一引数が WKWebView への参照を保持していれば循環参照が起きてしまいます。
まさに上記のようなコードです。
対応策
今回は下記のクラスを用意して擬似的に弱参照になるようにして循環参照を回避しました。
final class WeakScriptMessageHandlerReference: NSObject, WKScriptMessageHandler {
private weak var handler: WKScriptMessageHandler?
init(to handler: WKScriptMessageHandler) {
self.handler = handler
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
handler?.userContentController(userContentController, didReceive: message)
}
}
add メソッド呼び出し部分を変更
let reference = WeakScriptMessageHandlerReference(to: self)
webView.configuration.userContentController.add(reference, name: "hoge")
まとめ
いつまた同じようなことが起きるか油断できないので定期的にメモリ使用量とメモリグラフを確認しましょう。
参考
Apple の公式リファレンス – add(_:name:)
WKWebView causes my view controller to leak – Stackoverflow