個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Xamarin に関する事を中心に書いています。 Microsoft MVP for Development Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

Xamarin.iOS Deep Dive その4 Wrapper type と User type

はじめに



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


www.slideshare.net


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


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

hiro128.hatenablog.jp



前回は、ネイティブとマネージドの話でした。そして、ネイティブオブジェクトにはさらに2種類のオブジェクトがあります。今回はその説明となります。


Wrapper type と User type


Xamarin.iOS で、取扱いに気をつけなくてはいけないのは、ネイティブオブジェクトですが、ネイティブオブジェクトにはさらに、Wrapper type と User type という2つの型があります。Xamarin.iOS における GC の挙動を考える上で、この2つの型の区別はとても重要になります。

Wrapper type


UIView や UIButton のような Objective-C の型をラップした型です。
モリーリークはしません。

public override void ViewDidLoad()
{             
    base.ViewDidLoad(); 

    var button = new UIButton();
}

Wrapper type の特徴


f:id:hiro128:20190930193636p:plain

  • マネージドの世界では、ネイティブオブジェクトのインスタンスへのハンドルだけのみを持ちます。(マネージドな世界だけで保持されているステートは持ちません)
  • Wrapper type のインスタンスの寿命は、ネイティブオブジェクトの寿命とは無関係です。
  • Wrapper type のインスタンスは、マネージドな世界にネイティブオブジェクトへのハンドル以外には何も保持していないので、GC の判断によっていつでも自由に解放できます。
  • もし、後で再びそのオブジェクトが必要になった場合には Wrapper type のインスタンスが再度作成されるます。
  • Wrapper type としてインスタンス生成されても、イベントのアタッチなどでマネージドな世界だけで管理されているステートを持ったタイミングで、User type にアップグレードされます。



User type


f:id:hiro128:20190930193654p:plain

下記の MyButton のように、UIView や UIButton のような Wrapper type を継承し派生した型で Objective-C に対応する型が無いものです。
いつマネージ側の参照が解放され。ネイティブオブジェクトが破棄されるかがポイントです。(メモリーリークのリスクがあります)

public override void ViewDidLoad()
{             
    base.ViewDidLoad(); 

    var button = new MyButton();
}

public class MyButton : UIButton 
{
    public string Id { get; set; }  
}

User type の特徴

  • マネージドの世界では、ネイティブオブジェクトのインスタンスへのハンドルの他に、マネージドな世界だけで管理されているステートを持ちます。
  • User type のインスタンスの寿命は、ネイティブオブジェクトの寿命と関連がります。
  • User type のインスタンスは マネージドな世界だけで管理されているステートを持つ可能性があるため GC の判断によって自由に解放することはできません。
  • Xamarin.iOS はマネージオブジェクトを必要な間はずっと生かし続けることを保証ために GCHandle を作成し、回収されないようにします。


今回は以上となります。


次回は User Type のオブジェクトが破棄される状況をご説明します。

Xamarin.iOS Deep Dive その3 ネイティブとマネージド

はじめに


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


www.slideshare.net


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


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

hiro128.hatenablog.jp



今回から、Xamarin.iOS がメモリーリークするメカニズムについての説明に入っていきます。


今回のお話に役に立つ iOS アプリのアーキテクチャーについてはこちらの公式ドキュメントも是非ご参照ください。
docs.microsoft.com

Xamarin.iOS で大変なのは、プラットフォーム固有の部分だけ

まず、最初に大事なことを確認しますと、Xamarin.iOS で、メモリリークやイベントの消失など「クセ」があり大変なのは、プラットフォーム固有の部分だけです。.NET の世界で閉じていることの多いロジック部分は、センサー類などの機種依存コードのDI部分を除けばとても安定しています。



f:id:hiro128:20190927204923p:plain



そして、プラットフォーム固有の部分とは、具体的には以下の2つです。

NSObject を継承したクラス
public class SomeClass : NSObject
{

}

※ NSObject 自体も、 INativeObject を実装しています。

INativeObject を実装するクラス
public class SomeClass : ISomeInterface
{

}

public interface ISomeInterface : INativeObject
{

}

この2つの共通点は、ネイティブオブジェクトつまり Objective-C の世界へのポインター(ハンドル)を持っていることです。ネイティブの世界へのハンドルを持っているため適切に扱わないとからリークします。

Windows の世界でも FileStream など、Win32 API の世界へのポインターを持つものを適切に扱わないとリークしてしまうのと同じことです。

INativeObject の定義
using System;

namespace ObjCRuntime
{
    //
    // 概要:
    //     A simple interface that is used to expose the unmanaged object pointer in various
    //     MonoTouch classes.
    //
    // コメント:
    //     All this interface requires is for a class to expose an IntPtr that points to
    //     the unmanaged pointer to the actual object.
    public interface INativeObject
    {
        //
        // 概要:
        //     Handle (pointer) to the unmanaged object representation.
        //
        // コメント:
        //     This IntPtr is a handle to the underlying unmanaged representation for this object.
        IntPtr Handle { get; }
    }
}



ネイティブとマネージド

ネイティブとマネージドの定義は以下の通りとなります。

ネイティブ

  • iOSのプラットフォームでネイティブに実行されるコード
  • INativeObject を実装するクラス

ネイティブ

  • mono ランタイムによって実行が管理されるコード
  • INativeObject を実装しないクラス


そして、Xamarin.iOS(マネージド)の世界の裏には隠れた Objective-C(ネイティブ)の世界がある事を常に意識してください。この表と裏の世界との関係性によって、メモリーリークが発生します。


f:id:hiro128:20190927204833p:plain




それでは、今回は以上となります。


次回はこちらです。
hiro128.hatenablog.jp

Xamarin.iOS Deep Dive その2 Xamarin.iOS は簡単にメモリーリークしてしまう

はじめに


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


www.slideshare.net


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


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

hiro128.hatenablog.jp



今回は Windows で 開発してきた皆さんが Xamarin.iOS でハマりがちなメモリーリークについてご説明します。

Windows と Xamarin.iOS の簡単なサンプルコードでメモリーリークの状況をそれぞれ確認する。

View 上にボタンがあり、ボタンを押すと View を閉じるサンプルコードです。 Windows と Xamarin.iOS で全く同じことをしています。

実際に動作するサンプルコードは GitHub にアップしてありますので是非動かしてみてください。GitHub にアップしたサンプルではオブジェクトの初期化、破棄、ファイナライズでそれぞれトレースを出力するようにしています。 blog のコードは簡略化してあります。

ますは Windows のコードです。

Windows Forms
void InitializeComponent()
{
    var buttonClose = new Button();
    buttonClose.Click += ButtonClose_Click;
    Controls.Add(buttonClose); 
} 

void ButtonClose_Click(object sender, EventArgs e) => Close();

フルバージョンはこちらです。
github.com


とても簡単なコードですね。当然、メモリーリークなどしません。

次に、Xamarin.iOS で同じことをやってみましょう


Xamarin.iOS
public override void ViewDidLoad()
{
    base.ViewDidLoad();

    var dismissViewButton = new UIButton();
    dismissViewButton.TouchUpInside += DismissViewButton_TouchUpInside;
    View.AddSubview(dismissViewButton); 
} 

void DismissViewButton_TouchUpInside(object sender, EventArgs e)
    => DismissViewController(truenull);

フルバージョンはこちらです。
github.com

はい、メモリーリークしました!

全く同じ意味のコードを書いているのに、Xamarin.iOS だけメモリーリークしてしまいます。

ただし、実はこの現象は、どちらかといえばメモリーリークする Xamarin.iOS の方がプログラムの動きとしてはむしろ当たり前で、メモリーリークしない、Windows がすごいと言った方が正確です。Windows .NETGC はなかなか賢くて、このような単純なパターンであれば本来メモリーリークしてもおかしくないコードでもしっかり GC が回収してくれます。

なぜ、Xamarin.iOS ではメモリーリークするのか


前回も言った通り、Xamarin.iOS は「攻めた設計」であり、.NET と iOS API の相互運用という 「禁断の果実」に手を出しているからメモリーリークしてしまいます。

具体的には

という機能を使えるようにした「副作用」です。

では、次回以降では、Xamarin.iOS でメモリーリークする詳しいメカニズムを見ていきましょう。

今回は以上となります。


次回はこちらです。
hiro128.hatenablog.jp

Xamarin.iOS Deep Dive その1 Xamarin.iOS の特徴

はじめに


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


www.slideshare.net


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


iOS アプリのアーキテクチャーについてはこちらの公式ドキュメントも是非ご参照ください。
docs.microsoft.com


Xamarin.iOS は「攻めた設計」で「禁断の果実」に手を出している

今まで Windows アプリを開発者していた方が Xamarin.iOS を使い始めたとき多くの方がぶつかるトラブルがメモリーリークです。では、なぜ Xamarin.iOS はメモリーリークしやすいのでしょうか。 それは、Xamarin.iOS が「禁断の果実」に手を出しているからです。「禁断の果実」とは、.NET と Objective-C との相互運用です。これを実現するために、「攻めた設計」で、様々な「無理」をしているために、このような問題が起きてしまいます。

下の図を見てください。Xamarin.iOS では、ユーザが明示的に、バインディングによって C# から iOS API を利用することができ、逆に、Objective-C ランタイム側からも、mono ランタイム上で動く C# で書いたメソッドをコールしたりプロパティにアクセスさせることができます。


f:id:hiro128:20190918194901p:plain


この相互運用機能を実現したことが「攻めた設計」であり、「禁断の果実」と言えます。


ですが、決して Xamarin.iOS の設計が間違っているわけではありません。そこにはトラブルの起きやすさと引き換えに手に入れた、便利な機能があります。そういう意味で、Xamarin.iOS を効果的に使うにはしっかり仕組みを理解することが必要となります。しっかりと理解して利用すればトラブルは発生しませんが、完全に使いこなすにはかなり深いところまでを理解する必要があるため、これまで Xamarin.iOS を使ってきた総仕上げとして知る限りのことを記録できればと考えています。

Xamarin.iOS の優れている点

iOSOEM Widgets や API をすべて利用でき、しかも .NET の機能も織り交ぜながら、機能拡張できます。


iOSOEM Widgets の機能を .NET の機能も織り交ぜながら、拡張できるのはとても強力です。できることの幅がとても広がります。難易度は別として Xamarin.iOS に起因して何かができなくなることはまずありません。ちょっとした便利機能を追加するのも簡単ですし、各種 MVVM のライブラリや Rx のライブラリ等自分の好みの機能を追加して利用することができます。また、View Model や Model と上手く連携することで、Xamarin の本領である Android との共通化にも大きく寄与します。

プロトコルセレクター など、Objective-C ランタイムのコアな機能を使うことができます。


プロトコルセレクターに相当する機能は .NET 側にもありますが、アプリを開発していくと敢えて、iOS 側の機能を使いたくなるケースがあります。
そのような時、Xamarin.iOS では自分でプロトコルを定義できるので、iOS 側に寄せる部分、.NET 側に寄せる部分を整理することで構成をスッキリさせることができます。iOS の機能を使うか、.NET の機能を使うか、その選択を自分で選べるのは大きな利点です。

Xamarin.iOS の使いにくい点

自由度が高いため、よく理解していないで使うと、かえって負債を産んでしまいます。

ネイティブでできることはほぼ全部できるのと同時に、そこに .NET も混ぜることができるので、非常に複雑化しやすくなります。しっかり考えて使わないとグチャグチャな構造になり、手が付けられなくなります。

使い方を間違えるとメモリーリークやイベントの破壊などが起きる仕様を抱えています。

これは相互運用を行わず、Objective-C ランタイムの中の閉じた世界とすれば、このような問題も起こりませんでした。

まとめ

Xamarin.iOS は効果も問題点もすべてを理解して使わないとその実力を発揮できません。

苦労も多いですが、C# ユーザーが、iOS のアプリを書きたいときに知らない言語、知らない OS、知らない API で始めるのがつらい場合、チャレンジする価値は十分にあります。なぜなら、.NET が簡単すぎるだけで、Xamarin.iOS でおこるメモリーリークなどのトラブルは色々なプログラム言語を習得していけば普通に遭遇する事象であり、特別なものではありません。よって、それをこの機会にしっかり理解しておけば他の言語などを理解するときにとても役に立つからです。


今回は以上となります。


次回はこちらです。
hiro128.hatenablog.jp

Microsoft Most Valuable Professional を再受賞しました。

皆様こんにちは、@hiro128_777です。


おかげさまで、今回も Microsoft Most Valuable Professional を再受賞することができ、なんとか MVP として 3期目を迎えることができました。


これも、ハンズオンに参加くださった方々、登壇に誘っていただいた方々、イベントにご参加いただいた方のおかげです。


本当にありがとうございます。


MVP を2年間続けたことで色々な方とお知り合いになることができ、面白そうなネタや有益な情報もたくさん頂けました。そのおかげで、自分のエンジニアとしての可能性もとても広がったと実感しています。


そして何よりもエンジニアライフを楽しめていることがうれしいです。


正直、私は凡人のエンジニアですが、凡人でも楽んで活動することで、思いがけない評価もいただけますし、自分のエンジニアライフもとっても楽しくなりますので、自分の活動を通じて少しでも自分のエンジニアライフが楽しくなる方が増えればいいなと思っています。


せっかくエンジニアという職業を選んだのですから、楽しめることが一番ですよね。


今年度は、IoT と サーバーレス、App Center を組み合わせたハンズオンを準備中です。


8月にはハンズオンを開催しますので、その時はよろしくお願いします!

Visual Studio 2019 for Mac version 8.0 正式版が がリリースされました!

4月2日に、みなさん待望の Visual Studio 2019 for Mac version 8.0 正式版が がリリースされました!
(その後、4月4日に軽微なバグのアップデートがリリースされています。)

visualstudio.microsoft.com



Windows 版での目玉機能は何と言っても、

  • AI がコーディングを支援する「IntelliCode」
  • 複数人でコードをリアルタイムに共有し、共同編集できる「Live Share」

ですが、なんとこの2つの機能の両方ともが、Mac 版には実装されていません(笑)


プレビュー版の時点で、流石にそれはね〜だろ〜ということで、異議申し立てしている方もおりましたが、間に合いませんでした。



Live Share への魂の叫び(笑)

twitter.com


IntelliCode への魂の叫び(笑)

twitter.com



ですが、まあこれは仕方ないんです。Visual Studio 2019 for Mac は元「Xamarin Studio」であり、そもそものコードベースが違うので、どうにもなりません。

Live Share については、Mac では VS Code で可能なので、そちらを使えということでしょうかね。

ただ、今回設定で、「全般」 -> 「New editor preview」をいじればコードエディタが、Windows と同じコードベースの「新しいコードエディタ」が使用できるようになっているので、その他のコンポーネントも順次 Windows と同じコードベースに置き換えられていけば、いずれかの段階で可能になるのでは?と思っています。


今後の Visual Studio 2019 for Mac のロードマップは下記ですが、現在のところ、Live Share, IntelliCode への言及はない状態です。


docs.microsoft.com




その他リリースノートは以下の通りです。

docs.microsoft.com



私も早速アップデートしまして、Visual Studio 2019 for Mac での開発に切り替えました!

3月23日(土)に、Windowsアプリを開発している皆さん、C#でスマホアプリ開発を試してみませんか?ハンズオン を開催いたしました

こんにちは、@hiro128_777です。

3月23日(土)に、Windowsアプリを開発している皆さん、C#スマホアプリ開発を試してみませんか?ハンズオン を開催いたしました!

jxug.connpass.com

今回は22名の方々にご参加いただきました。

@nakasho_dev さんにはメンターとしてご協力頂き非常に助かりました。この場をお借りして御礼申し上げます。本当にありがとうございました。

今回は iOS Android 両方のアプリを作成するハンズオンでしたが、特にAndroidエミュレータに関する問題に苦戦しました。

なお、毎度のことですが、コーディングではあまり躓きは少なかった印象です。

アンケート結果

ハンズオンを最後まで完了できたか

f:id:hiro128:20190328181026p:plain


みなさん最後まで進めることはできましたが、エミュレータのトラブル、残念ながら環境のトラブルでデプロイができなかった方がいらっしゃいました。



難易度

f:id:hiro128:20190328181059p:plain


難易度についてですが、84%の方が「簡単」「ちょうどいい」とご回答され、16%の方が「難しい」とご回答されました。


時間

f:id:hiro128:20190328181118p:plain


時間ですが、78%の方が「ちょうどいい」または「短い」とご回答いただいております。


ハンズオン自体は、休憩などを省くと正味3時間程度でした。集中力的にも3時間程度がちょうどいい長さだと感じました。


役に立ったか

f:id:hiro128:20190328181143p:plain


なんとみなさま全員に「役に立った」とご回答を頂けました!
主催者としては非常にうれしい結果となりました!



その他のご感想

その他のご感想としては、以下をいただきました。

  • 対象OSがあれば事前調査していただきたかった。(Windows7で動作確認まで行えなかったため)。作りの大枠が理解できてよかった。
  • androidエミュレータに苦戦しました
  • 自由に質問が出来る時間が欲しかったです
  • 時間が短いこともあり、駆け足感は仕方がないかと思いますが、コードの説明がもう少しお願いしたかったと感じました。しかし、このような機会はあまりないのでとても参考になりました。ありがとうございました。
  • 実際に手を動かして開発したのは初めてだったので、経験できたのは良かった。
  • ケーブルを忘れたため、実機ではテストできませんでしたが、シュミレーターで確認出来たので良かったです。
  • 非常にわかりやすかったです。ありがとうございました。
  • MacがなくてもiOSのネイティブアプリを開発できる日を心待ちしています。
  • 以前からXamarinに興味はあったものの、どう作ればよいのかがわからないままでした。今回参加して、いろいろお話を聞けてとても勉強になりました!
  • 自主勉強でXCode上でコーディングを行ったことがありましたが、Swiftとストーリーボードとの格闘に四苦八苦し、開発を諦めてしまったことがありました。私は今C#のWebアプリ開発に携わっているため、C#の知識を流用できるならと思い、Xamarinのことを勉強したいと考えていました。改めて、Xamarinのメリットを学んだ上でハンズオンを体験したことで、スマホアプリ開発の興味が再び湧いてきました。本日はありがとうございました!
  • ご丁寧にありがとうございました。
  • モバイルアプリ初めてでしたが実機確認までできました! モバイルアプリエンジニア転職に向けて勉強がんばります。Xamarinを知るためにSwift, Android開発にも手を出してみようと思います。


また、今後開催してほしいハンズオンとしては、以下をいただきました。

  • 今回のようなハンズオンをお願いします
  • アプリストアへの申請なども含め、難易度を上げたハンズオン。
  • AIと組み合わせたアプリ開発のハンズオンがしてみたいです!
  • 質問・相談会などがあれば。。。
  • いまいまは思いつかないです。
  • c#visual studio) を使ったWEBアプリの作り方(SQL DBのCRUDMVCの使い方、スマホWeBブラウザでも見やすくする工夫 など) を教えていただきたいです
  • Prismを用いての設計方法・Xamarinでのテストの書き方・自動テスト実行環境
  • さきほどのWeBアプリのハンズオンについての追加希望です。環境は、Azure の WEB APP、SQL DB を使ったやり方を教えていただきたいです


皆様のご意見を参考に今後も有意義なハンズオンを開催できるように精進いたします。

最後にお忙しい中休日にお時間を作ってご参加頂いた皆様、本当にありがとうございました!

また、JXUGのイベントでお会いできるのを楽しみにしております!