WKWebView の WKUserContentController に気をつけろ!

概要

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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です