個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Microsoft MVP for Developer Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

Xamarin.iOS Deep Dive その5 User type のオブジェクトが破棄される状況を理解する

はじめに



こんにちは、@hiro128_777です。
iOSDC で 以下の通り Xamarin.iOS の仕組みについてお話させていただきました。


www.slideshare.net
www.youtube.com



ですが、時間が足りなくて話しきれなかったことが多いので、blog でもう少し詳しく説明していこうと思います。


前回のお話はこちらです。

hiro128.hatenablog.jp


前回は、Wrapper type と User type の話でした。今回は User type の挙動について深く見ていきます。

User type でマネージ側の参照が解放される状況


前回、User type のオブジェクトは GC の判断で自由に開放することはできないというお話をしました。では、User type オブジェクトはいつ解放されるのでしょうか。

解放される状況は実質以下の2つです。

  1. 他からの参照がない状況で Dispose が明示的に呼び出されたとき
  2. ネイティブオブジェクトへの参照が対応するマネージオブジェクトのみになり GC がオブジェクトを解放したとき


他からの参照がない状況で Dispose が明示的に呼び出されたとき

C#
var button = new MyButton();

button?.Dispose();


上記のように、buttonを保持している他のマネージオブジェクトが何もないときに、ユーザーが明示的にDisposeをコールすると、マネージオブジェクトの参照が外れ、ネイティブオブジェクトとマネージオブジェクトの間のリンクが切断されて、GC がそのオブジェクトを回収できるようになります。

この時、ユーザーがDisposeをコールした後、ネイティブコードが何らかの理由でそのオブジェクトを使用しようとすると、Xamarin.iOS は対応するマネージオブジェクトが存在しないことを検出し、それを再作成しようとします。ですがこれは失敗し、プロセスを終了させる例外がスローされてしまいます。

その問題を解決するため、ユーザーがDisposeを呼び出したとき、Xamarin.iOS は以下のような挙動をします。

  1. マネージオブジェクトの参照を外す。(まだGCに回収はされていない)
  2. ただし、ネイティブオブジェクトの参照カウンタが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 のオブジェクトは破棄されます。


今回は以上となります。


次回は、イベントのアタッチによってメモリーリークするメカニズムを手順を追ってご説明します。