Xamarin.iOSでUITableViewの罠 / Xamarinの進化に望む事に、C#のイベントとobjc(UIKit)のデリゲートを同時に使おうとすると罠にはまる、という記事がありました。ほうほうたしかにこれはハマりそうな所なので、実際どんな動きをしているのか確かめてみました。
TableView.WeakDelegateが切り替わってた
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
Console.WriteLine ("A1 delegate == nil? {0}", (this.TableView.Delegate == null) ? "YES" : "NO");
Console.WriteLine ("B1 weakDelegate={0}", this.TableView.WeakDelegate.GetType ());
this.TableView.DecelerationStarted += (sender, e) => {
Console.WriteLine ("scrolled.");
};
Console.WriteLine ("A2 delegate == nil? {0}", (this.TableView.Delegate == null) ? "YES" : "NO");
Console.WriteLine ("B2 weakDelegate={0}", this.TableView.WeakDelegate.GetType ());
}
TableView.Delegateは、A1,A2共にnilでした。ですがweakDelegateはB1とB2ではオブジェクトが切り替わっています。
A1 delegate == nil? YES
B1 weakDelegate=MyTasky.NumberTableViewControllerController
A2 delegate == nil? YES
B2 weakDelegate=MonoTouch.UIKit.UIScrollView+_UIScrollViewDelegate
Xcodeで書いてる時に馴染みのあるdelegateですが、MonoTouchのUITableViewではWeakDelegateが該当しているようです。objcの弱参照だから"Weak"Delegateなのでしょう。
このWeakDelegateが、C#のイベント登録前後で切り替わっています。
推測ですが、以下の挙動をしています。
- 初期状態では、WeakDelegateが自分自身なので、デリゲートのコールバックは自クラスとなっている。
- なので自クラスにデリゲート部分を実装していれば、それが呼び出される。 objcのUITableViewControllerにデリゲートメソッドを書くのと同じ感覚。
- C#のイベント登録
xxx += (sender, e) => {...}
したタイミングで、WeakDelegateがC#のイベント登録されたDelegateにすり変わる。なので、自クラスのデリゲートのコールバックは呼ばれなくなる。
元の記事にある「Objective-Cとc#でのイベント実装思想の違いを把握してないと気付けなさそう」はまさにその通りですね。
ではTableView.Delegateを使ったらどうなるのか?
WeakDelegateとDelegateがあるので、じゃあDelegateを使ったらどうなるか?と試したのですが、WeakDelegateの時と同じで、片方が片方を隠してしまいました。
- TableView.Delegate を登録してから C#のイベント を登録 → C#のイベントが有効
- C#のイベント を登録してから TableView.Delegate を登録 → Delegateのイベントが有効
混ぜるな危険って事なんですね。
この手の罠一覧が欲しい
「この先地雷原」が分かる、地図のような資料があれば、まず見ておきたい所です。
その他メモ書き
- objcのUIKitとMonoTouchのUIKitの、メソッド対応表というか「命名規則変換ルール」が欲しい。
- objcの"will〜"はMonoの"〜Started"とか。
- C#のイベント登録
+=(){}
はまだよく分からない。 - XamarinStudioで、「このクラスのAPIリファレンス」を表示するショートカットキーは何所だ。
- XamarinStudioで日本語入力していると、変換直後のキー入力1個欠落してるみたい→コメントに日本語書いてると変になるのだけど、自分だけかな?