Xamarin for Visual Studioを使ってみた

Xamarinが無料化され、WindowsではVisualStudioと統合されたとのことで軽く使用してみました。

自分の環境ではVisual Studio Commynity 2015を使用しています。

File->New->Projectから、 Templates->Visual C#->Cross Platform->Blank App(Xamarin.Forms Portable)を選択し、 空ののプロジェクトを作る

3d1b0acc262718ab1c2743fadfe4258c.png

まずは、このままビルドして実機で動かしてみようとしたら、 以下のエラー。。。

b1eaca4e48fdaadbde31d8674d0b2cde (1).png

何もしていないのになぜエラーとなるのか。 なんというか、こういうのがMicrosoftは雑な気がする。

調べたところ、C:\Users\ユーザー名\AppData\Local\Xamarin\配下のファイルを全削除すると良いらしい。 削除後にビルドすると上記フォルダのファイルが再ダウンロードされ無事にビルドが通って、 動作確認できました。

golangではスタックとヒープを気にする必要が無い

調べようと思ったきっかけは、golangでは以下のように ローカル変数のアドレスを戻り値としても問題ないということ。

package main

import (
    "fmt"
)

type Animal struct {
    Name string
    Age  int
}

func main() {
    animal := alloc()
    fmt.Printf("allocate animal structure %p", animal)
}

func allocAnimal() *Animal {
    return  &Animal{}
}

ポインタを扱えるC/C++ではローカル変数のポインタを戻り値とした場合、スタック領域のポインタを関数外に渡すため、 コンパイル時点で警告が表示されます。
(なぜエラーにしない)
実行時には最悪、セグメンテーションフォールトで落ちます。
そのため、mallocやnewでヒープ領域にメモリを割り当てる必要があります
なぜ、golangではこれが許されるのか気になり調べてみました。

スタックとヒープについて

golangにおけるスッタックとヒープについて簡単にですが、説明します。

スタック

  • 確保・解放が早い
  • 寿命が短い。関数内もしくは特定のスコープ内限定
  • サイズが小さい
  • ローカル変数・引数などで使われる

ヒープ

  • 確保・解放が遅い
  • 寿命は自由
  • サイズが大きい
  • newなどで確保される

golangのFAQ

golangのFAQにはスタックとヒープの扱いでは以下のように記載されています。 ※全文はリンク先にて参照ください

FAQ - golang.jp

正確さを期するなら知る必要はありません。Goの各変数は参照されている限り存在し続けます。Goの実装によって選択された格納場所がどこかは、Go言語的には意味を持ちません。

と、どうやらコンパイラが適宜判断して、関数内に収まる場合はスタックに、関数外でも参照される変数はローカル変数でもヒープに割り当てられるようです。

実際に調べてみた

コンパイラにフラグを渡すと、メモリ割当の様子がわかります。

go build -gcflags -m hello.go

サンプルコードそのままの様子を見てみます。

./main.go:17: can inline allocAnimal
./main.go:13: inlining call to allocAnimal
./main.go:14: animal escapes to heap
./main.go:13: &Animal literal escapes to heap
./main.go:14: main ... argument does not escape
./main.go:18: &Animal literal escapes to heap // ローカル変数がヒープに

太字の箇所でコンパイラが関数の外で扱われると判断し、スタックではなくヒープに領域を確保しています。

newで確保したメモリを関数の外に出さない場合

では、コードを以下のようにするとどうでしょうか。

func allocAnimal() *Animal {
    animal := new(Animal)
    animal.Name = "Cat"
    animal.Age = 23
    return &Animal{}
}

このコードの内容にはあまり意味はありませんが、newしたものを関数内で完結させています。 このコードの様子を再度出力してみます。

./main.go:17: can inline allocAnimal
./main.go:13: inlining call to allocAnimal
./main.go:14: animal escapes to heap
./main.go:13: &Animal literal escapes to heap
./main.go:13: main new(Animal) does not escape newしてもヒープに割り当てられない
./main.go:14: main ... argument does not escape
./main.go:22: &Animal literal escapes to heap
./main.go:18: allocAnimal new(Animal) does not escape

golangの場合はnewで確保しても関数内で完結するものはヒープではなくスタックに割り当てられるようです。

このように、golangでは特にスタックかヒープかというのを特に意識しなくても問題はないようです。 長く、C/C++をやっていた人間からするとモヤモヤしますが。。。

MonoBehaviourの使用していないマジックメソッドは消すこと

MonoBehaviourのマジックメソッドとは

Awake/Start/UpdateなどMonoBehaviourにもともと備わっているメソッドのことです。 Unity上から新規にスクリプトを追加する場合はデフォルトでStartとUpdateが追加されています。

なぜ消すのか

マジックメソッドのタイミングでなんらかの処理を実行する必要がある場合は問題ありません 問題なのは、メソッドの中身が空なのにそのメソッドを残すことです。 公式ブログには以下のように記載されています。

blogs.unity3d.com

任意の型のMonoBehaviourが初めてその基底のスクリプトにアクセスしたときに、スクリプティングランタイム(MonoもしくはIL2CPP)によって何かのマジックメソッドが定義されているかを調査され、この情報がキャッシュされます。もしMonoBehaviourが特定のメソッドを持っていたら所定のリストに追加されます。例えば スクリプトがUpdateメソッドを持っていたら「毎フレームUpdateを呼ぶべきスクリプトのリスト」に追加されるわけです。

つまり、コンパイル時点でマジックメソッドを持っているスクリプトをキャッシュし、 そのメソッドをコールするリストに追加されて、何も処理をしていないのに無駄にコールされコストがかかってしまうのです。

そのため、キャッシュされないように使用しないマジックメソッドは削除するようにしましょう。

※リンク先のUpdateをCallする方法も参考になります