iOS 9~12 で WKWebView 初期化直後の参照カウント数について調べた


以前の記事:
https://qiita.com/srea/items/0d935b0bad4509ddbc50

iOS 11 では、内部の何らかの仕様変更により強参照される仕様に変わっていました。

この記事は続編で iOS 12 ではそのへんの仕様はどうなっているのかを確認しました。

WKWebView と UIView を初期化して weak プロパティに代入してみる

検証コード

import UIKit
import WebKit

final class ViewController: UIViewController {

    weak var wkWebView: WKWebView!
    weak var uiView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        wkWebView = WKWebView()
        print("WebView: \(CFGetRetainCount(wkWebView))")

        uiView = UIView()
        print("WebView: \(CFGetRetainCount(uiView))")
    }
}

「weak プロパティのため、すぐ解放されます」と表示されています。
通常であれば、 weak プロパティに代入し、そのあと Print 関数で参照カウント数を取得しようとすると、すでに解放されているためクラッシュするはずです。

では、OS バージョン毎に動作を確認していきます。
どういう結果になるでしょう。

iOS 9.3

クラッシュしました。正しい動きです。

コンソールログ

(lldb) 

iOS 10.3.1

クラッシュしました。正しい動きです。

コンソールログ

(lldb) 

iOS 11.4

クラッシュしました。が、WebViewは解放されていないようで、参照カウントが2と表示されています。

コンソールログ

WebView: 2
(lldb) 

iOS 12.0

クラッシュしました。が、WebViewは解放されていないようで、参照カウントが2と表示されています。

コンソールログ

WebView: 2
(lldb) 

結果比較表

iOS 12 11 10 9
WKWebView 初期化時の参照カウント 2 2 0 0

WKWebView のデバッグメモリグラフ機能を使ってメモリに展開されたオブジェクトを確認する

検証コード

import UIKit
import WebKit

final class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let webView = WKWebView()
        print("WebView: \(CFGetRetainCount(webView))")
    }
}

iOS 9.3

WebKit (43)

iOS 10.3.1

WebKit (48)

iOS 11.4

WebKit (53)

iOS 12.0

WebKit (2)
WebKit (55)

まとめ

  • 差分から iOS 12 でも内部構成に変更が入っていることがわかる

結論

  • よくわからないが、 iOS 11 から WKWebView は WebKit システム内から強参照されるようになった?
  • 循環参照が発生する訳ではないため、 ViewController のプロパティで強参照して問題は無い。

完全に余談

iOS 12 で JSで通信出来ない evaluateJavascript 現象を確認しています。
条件としては、
- 実機 ( Simulator は動く)
- WKWebView を JS の実行のためだけに使っている
- WKWebView をどこにも AddSubview していない又は、AddSubView していても、そのViewController はインスタンス保持のみで表示に使われていない

この場合、 WKWebView を frame zero で AddSubview することで動くようになりました。

Original