Help us understand the problem. What is going on with this article?

Xamarin.iOS の AOT に気をつけろ!Rx の FromEventPattern を使うときの注意点

More than 3 years have passed since last update.

Reactive Extensions の FromEventPattern を使うと、イベントを IObservable に変換できて、複数のイベントに時系列な関係を与えたり、他のストリーム処理とシームレスに扱えたりします。

Xamarin .iOS でも .Android でもこの機能を使うことができて大変便利ですが、Xamarin.iOS の場合 AOT による制限に気をつける必要があります。

以下は、なんの変哲もない、「ボタンを押したらタイトルを ”Clicked!” に変える」コードです。

MyButton.TouchUpInside += (s, e) => MyButton.SetTitle("Clicked!", UIControlState.Normal);

これを FromEventPattern を使うとこう書けます。

DoesNotWorkOnDevice.cs
Observable.FromEventPattern(MyButton, "TouchUpInside")
.Subscribe(x => MyButton.SetTitle("Clicked!", UIControlState.Normal));

このコード、iOSシミュレータでは正常に動作しますが、 実機では、ビルドは通りますが動作しません。 実行時にこんなエラーがでます。

System.InvalidOperationException: Could not find event 'TouchUpInside' on object of type 'MonoTouch.UIKit.UIButton'.

TouchUpInside が無いと言われます。

これは AOT により生成されたコードに、このイベントが含まれないのだと推測します。イベント名を文字列リテラルで指定しているので、そこまでの解析は期待できないですよね。

シミュレータで動作したのは、この場合は AOT でなく JIT で動作しているため。以下でも言及されています。

対象がiOSシミュレーターである場合と、iOSデバイスである場合とで、大きく異なる。iOSシミュレーターは、エミュレーターではなく、あくまでMac OS Xが動作しているx86 CPUの上で動作している仮想マシンであり、アプリケーションはJITによって動作する。iOSデバイスはARMであり、iOSデバイス用にビルドされたアプリケーションはAOTによってARMのCPU命令に変換されており、ARM上でしか動作しない。

Xamarin.iOS では実機で動作させないと安心ならないと言われる所以です。

さて、このケースでは、FromEventPattern の別なオーバーロードを使うことで解決です。

WorkOnDevice.cs
Observable.FromEventPattern(
  h => MyButton.TouchUpInside+=h, 
  h => MyButton.TouchUpInside-=h)
.Subscribe(x => MyButton.SetTitle("Clicked!", UIControlState.Normal));

Xamarin.iOS の制限事項は以下に。

これまでこの制限に引っかかった事がなかったのですが、初めて引っかかりました。

メソッドを文字列リテラルで書いた時点で私の負けです、本当にありがとうございました。

いくつか追記

@espresso3389 さんも、同様のことを書いておられました。 [PreserveAttribute] なるほど。

@nuits_jp さんも 調べてくれました。 現在のバージョン(Xamarin.iOS 9.8)でも同様です。また Xamarin.Forms では発生しないようですが、 これは Xamarin.Forms 内部で使われているから AOT に消されないというだけで、必ず消されない保証はありません。

Xamarin.Forms Behavior の公式ドキュメントに、下のようなコード例が紹介されているんですが、これも AOT で消される可能性を持っています、使うときは注意しましょう。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away