はじめに
こんにちは、@hiro128_777です。
iOSDC で 以下の通り Xamarin.iOS の仕組みについてお話させていただきました。
www.youtube.com
ですが、時間が足りなくて話しきれなかったことが多いので、blog でもう少し詳しく説明していこうと思います。
前回のお話はこちらです。
hiro128.hatenablog.jp
前回は、Wrapper type と User type の話でした。今回は User type の挙動について深く見ていきます。
User type でマネージ側の参照が解放される状況
前回、User type のオブジェクトは GC の判断で自由に開放することはできないというお話をしました。では、User type オブジェクトはいつ解放されるのでしょうか。
解放される状況は実質以下の2つです。
- 他からの参照がない状況で Dispose が明示的に呼び出されたとき
- ネイティブオブジェクトへの参照が対応するマネージオブジェクトのみになり GC がオブジェクトを解放したとき
他からの参照がない状況で Dispose が明示的に呼び出されたとき
C#
var button = new MyButton();
button?.Dispose();
上記のように、button
を保持している他のマネージオブジェクトが何もないときに、ユーザーが明示的にDispose
をコールすると、マネージオブジェクトの参照が外れ、ネイティブオブジェクトとマネージオブジェクトの間のリンクが切断されて、GC がそのオブジェクトを回収できるようになります。
この時、ユーザーがDispose
をコールした後、ネイティブコードが何らかの理由でそのオブジェクトを使用しようとすると、Xamarin.iOS は対応するマネージオブジェクトが存在しないことを検出し、それを再作成しようとします。ですがこれは失敗し、プロセスを終了させる例外がスローされてしまいます。
その問題を解決するため、ユーザーがDispose
を呼び出したとき、Xamarin.iOS は以下のような挙動をします。
- マネージオブジェクトの参照を外す。(まだGCに回収はされていない)
- ただし、ネイティブオブジェクトの参照カウンタが0に達するまで、ネイティブオブジェクトとマネージオブジェクトの間のリンクは切断しない。(よってGCは回収できない)
これにより、ネイティブオブジェクトが存続している限り、マネージオブジェクトを引き続き参照することができるようにしています
ネイティブオブジェクトへの参照が対応するマネージピアオブジェクトになり GC がオブジェクトを解放したとき
C#
UIButton button; public override void ViewDidLoad() { base.ViewDidLoad(); button = new MyButton(); View.AddSubView(button); } public override void ViewDidDisappear(bool animated) { base.ViewDidDisappear(animated); button?.RemoveFromSuperview(); button?.Dispose(); }
上記のような場合、button?.RemoveFromSuperview()
が実行されると、ネイティブオブジェクトの参照カウンタが1になります。さらに、マネージオブジェクトからの参照が唯一の参照であり、ネイティブコードは当該オブジェクトを再び使用しないと安全に仮定できます。よって、ネイティブオブジェクトとマネージオブジェクトの間のリンクを解除し、ネイティブオブジェクトを解放して、GC がマネージオブジェクトを回収できるようになります。
この2つの状態が発生した時、User type のオブジェクトは破棄されます。
今回は以上となります。
次回は、イベントのアタッチによってメモリーリークするメカニズムを手順を追ってご説明します。