SDD(Sleep-Driven Development)

睡眠の重要性!!睡眠の重要性!!

2024年まとめ

気づけば2024年も今日で終わりです。
今年で29歳だった私にとって2024年は色んな意味で変化のあった年でした。
来年で30歳になって記憶力が衰える一方なので今のうちから1年のまとめを残しておくべく、すっかり放置されたこのブログを更新しようと思います。

なお思いつきなので来年以降も更新されるかは謎。

1年のできごと

コンタクトをつけるようになった

後述の用事でコンタクトが欲しくなったので眼科で処方箋をもらいに行きました。
実は前もコンタクトを処方してもらいに眼科に行ったことがあるのですがその時は1時間粘ってもつけられるようにならず、心が折れて泣きながら帰ったのでリベンジでした。
結果は1.5時間粘って成功! 🎉
無事に処方箋をもらえました。

なおまたしても心が折れそうな中、看護師?のお姉さんに「がんばれ!がんばれ!」と成人してから初めてレベルの応援をされました。
あれがないとまたしても泣きながら帰ってたかもしれません。お姉さんありがとう。

スノボ

フリーランスの友人が平日からスノボに行って楽しそうなので連れて行ってもらいました。(日帰り)(私は有給)
場所は新潟のどこか(忘れたけど群馬寄り(?))。
このためにコンタクトをつけれるようにしました。

私はウィンタースポーツ初体験なくらいには完全に初心者だったのですが、初心者的なエリアで30分くらい滑って全然滑れなかった私に業を煮やした友人が普通に滑るような人がいるエリアに連れて行き放置プレイを食らいました。

結果、2時間かけてなんとか下まで着いた私が疲れ果てて終了。
滑れるようにはなりませんでした。

こう書くとなかなかひどい結果ですが、初めてのウィンタースポーツで滑れないなりに楽しめました。
敗因は圧倒的運動不足だったので体力をつけていつかリベンジしたいです。

あと滑ったあとの温泉は最高。

全然滑れなくて黄昏れる私

PC が壊れた

いい出来事ではないのですが、 PC が壊れました。

元々2020年に自作したやつでスペックは以下 👇

  • Ryzen 9 3900X
  • RTX 2070 Super
  • DDR4 32GB
  • M.2 SSD 1TB

なのですが、去年引っ越してから調子がちょっとずつ悪くなっていき、最近は BIOS すら起動しなくなりました。 原因はなんとなくわかっているものの、治すのがめんどく放置しています。

ただ来年のモンハンワイルズはやってみたいので BTO とかで新しい PC を買おうか悩み中(自作する元気はもうない)。

転職した

2020年10月に入社した会社を2024年8月をもって退職しました。

転職しようと思った理由は人間関係とか社内制度への不満とかありきたりですが、転職を決意したのは面白そうなお仕事だと思った会社の内定が貰えたことです。
どうしても隣の芝生は青く見えてしまう。
実際後述する理由で転職をして良かったと思いました。
前職もいいところだったけどね!

旅行(part1)

滅多に旅行しない私ですがときたま友人と遠出をしていて、今回は宮城に行ってきました。(日帰り)

私は神奈川に住んでいて東北の宮城というと結構離れている印象があるのですが、今の時代東京駅から 1.5時間から2時間弱で仙台駅まで着いちゃうんですね。すごい。

海鮮と牛タンを食べに行ったのですが、海鮮が美味しくて量を食べたのと食べる時間が遅かったせいもあり牛タンを食べるのが辛かった。美味しかったけど。
今度はちゃんと時間配分を考えて行きたいですね。

あと仙台うみの杜水族館にも行きました。
色々なお魚見れてよかった。

個人的にクラゲの雰囲気好き

🐈

ネコを飼いたいと8年くらい言い続けて、やっと今年飼い始めました。

品種は私がいいなーと持っていたベンガル

なおこのベンガルちゃん、引き取った当初は耳ダニ・ネコ回虫・風邪・気象由来の体調不良のコンボが重なりご飯を食べられないくらい弱ってしまっていました(元々のところが悪いというよりは、引っ越しのストレスの影響が大きそう)。

なので引き取って1ヶ月くらいは1週間に1〜2回動物病院に通っていました。
転職したことによりほぼ在宅になったのと、平日昼間でも中抜けできるようになったので(前職もできたけど申請がめんどい)通院が楽なのはとても助かりました。
ありがとう弊社。

今では避妊手術も終え、めっちゃ元気になりました。

避妊手術の影響でお洋服を着せられて物申したそうな弊ネコ

ネコがいることでだいぶ縛りも増えたけど、何よりかわいいので無問題。

旅行(part2)

親が還暦になるので記念(?)に三重県に旅行に行ってきました。(2泊3日)

当日に東京駅待ち合わせのはずが色々あって集合に失敗し、私ひとりだけで新幹線に乗るなどのアクシデントがあったものの、ご飯が美味しかったりいい景色が見れたりと個人的に満足。

なお伊勢神宮にも行ったのですが、ちょうど新嘗祭の時期だったらしく人が凄かったです。
伊勢神宮にお参りだけするなら新嘗祭の時期は外した方がよさそう。

人がめっちゃすごい(なお結構前の方で後ろもめっちゃ並んでる)

📸

前々から気になってはいたのと、ネコの記録を残しておきたいと思いカメラを買いました。

機種は α7C II。 元々 SONY が好きなのと、コンパクトな見た目が好きで選びました。

なお元々レンズは本体とセットになっていた FE 28-60mm F4-5.6 を使っていたのですが、主な被写体がネコの都合、シャッター速度を上げたりするとすぐに暗くなってしまうのがお悩みポイントでした。
そんな中、カメラに詳しい同僚に「単焦点 50mm F1.8 のレンズいいよ!」とおすすめされて試しに3万円くらいのレンズを買ってみたところめっちゃ明るく背景がボケたいい感じの写真を撮ることができめっちゃ感動しております。

なんかいい感じの写真が簡単に撮れる!

次は焦点距離変えられて明るいレンズが欲しくなってきた。。。(ネコはいい感じのことをしていても近づいたり動いたりするとそっちが気になってしまうので私は動けない)
こうやってレンズ沼というのは広く深くなっていくんだと理解させられました。めっちゃ楽しい。

そして元々聞いたことがあって興味があったので RAW 現像というのもやってみましたがこれも楽しい。
思わず Lightroom を契約してしまいました(Lightroom も記憶にあるより結構値上がりしてた)。
カメラ沼、無限に広がっている。。。

風景写真も好きで今後は風景も撮っていきたいのでおすすめのレンズがあれば教えてください。

まとめ

という感じで1年のできごとでした。 私にしては、変化の大きかった1年だったと思います。

基本的に私は静かに暮らしたい習性なので、来年はもうちょっと落ち着いた年になるといいなー。

Linuxに移行した話

この前macOSをアップデートしたところOSが飛びました。

まあOSが飛ぶのは別にいいのですが(よくない)、そのころはRootでログインできたりデグレってたりしていてmacOSに対する信仰心が薄れていたのと、ちょうどLinuxについての記事を多数見ていたので思い切ってLinuxに移行してみました。

その環境がようやく落ち着いてきたので備忘録代わりに。

環境

  • MacBookPro mid 2015(Core i7, RAM16GB, 512GB PCIeベースフラッシュストレージ)
  • Ubuntu 17.10

Ubuntu17.10はBIOSを破壊する致命的な不具合がありましたが、幸いにもMacBookProだったので免れた。

主にインストールしたアプリは以下。

i3-gaps

screenshot from 2018-01-03 17-34-05

いくつかのLinux記事で気になっていたタイル型のウィンドウマネージャであるi3、の改良バージョンであるi3-gapsを使っています。

タイル型ウィンドウマネージャはいくつかありますが、最近の記事は大体i3という印象だったのでi3、その中でもウィンドウ間の隙間を開けられるほうが個人的に好きなのでi3-gapsを選びました。

ウィンドウが重ならないので便利ですが世の中がウィンドウが重なる前提なのでたまにバグっぽい挙動をして辛い。

Compton

コンポジット型のウィンドウマネージャ、らしい。

i3のデフォルトだとウィンドウの透過ができなくて透け透け好き派としては微妙だったので導入しました。

アプリケーション毎にウィンドウの透過設定を変えられてめっちゃ便利です。

screenshot from 2018-01-03 18-16-26 ↑が導入前

screenshot from 2018-01-03 18-16-58 ↑導入後

(Google Cromeを透けさせたらさすがに見づらかったので透過設定をしてない) 透け透け好きには最高。

Polybar

screenshot from 2018-01-03 18-22-29

トップバー。i3にはデフォルトでバーは付いてくるのですが、こっちのほうが自由度が高い?らしいので導入。

シェルスクリプトとか使えるらしいですがまだ良くわかってないのでこれで放置。 どんなに充電しても95%止まりだったりボリュームが明らかにずれていても放置。

rofi

peek 2018-01-03 18-29 macOSで言うSpotlight。これもまた自由度が高くてシェルスクリプトとか使えるので重宝しています。

i3wm-themer

カラースキーマ。結構種類があって私はその中で暖色系のColorsを使っています。

環境構築用のシェルスクリプトもあるのですが、中途半端に不発だったのが残念。

ただGtk+のメニューバーの色やウィンドウの閉じるボタンのアイコンまで変更できるのは本当に便利。

ちなみにVimのテーマはなかったのでDespacioを使っています。

Terminal

ターミナルはUbuntuをインストールしたときにくっついてきたGnome-terminalを使っていて、エディタはVim、ShellはFishを使っています。

特にこだわりはないのでVimとかは(テーマを適用したくらいで)ほぼプレーン、、、なはずです。

JetBrains Toolbox

私はJetBrains信者なのでLinuxでもJetBrainsのIDEソースコードをがしがし書いています。

ぶっちゃけあまりタイル型のウィンドウマネージャと相性よくないですが、起動が心なしか早いのと、Linuxでも使える高機能IDEなのでずっと使い続けると思います(信者並感想)

Visual Studio Code

主にざっと確認したいけどVimだとちょっと辛いときに使っています。

軽い割に無駄に対応言語が多く、シンタックスハイライトのおかげで見やすいので重宝しています。

また、卒論でLatex書いてたときは大変お世話になりました。

Latex Workshop Extensionがなければ諦めていたと思います。卒論。

Typora

peek 2018-01-03 18-36

マルチプラットフォームなマークダウンエディタ。主にメモ用途に使っています。

2018年1月現在はβ版でその間は無料で使えるらしい?です。

テキスト+プレビューではなく、書いていくそばから変換されていく形式なので横幅をあまり使いたくない自分には便利。あとcssで見た目を変えられるので周りの色に合わせた感じにしています。

また、rofiと合わせて新規作成したりメモったファイルを開けるようにしています。

まとめ

最近「Linuxええで」という声をたまに聞くので思い切って移行してみたのですが、最大の学びは「WindowsmacOSはすごかった」でした。

i3wmは必要最低限の機能しかなく、Ubuntuにくっついてきたgnome-xxxが大活躍しました。それでもいくつか辛い部分があり、特に

が致命傷な感じです。

ただこれらも設定していけば解消するはず(トラックパッドは微妙だけど)であり、そこいらへんがLinuxの楽しいところだと思いました。

これからもLinuxを改造していき、どんどん便利(カオス)にしていきたいです。

最後に、設定ファイルもろもろをgithubに上げました。

ubuntu-i3wmというディレクトリが悪戦苦闘のあと設定ファイルの数々です。

誰かの参考になれば幸いです。

ただし、環境構築しながら書いたせいで何かおかしな部分があるかもしれません(現時点でいくつか見つけたのであとで修正します。あとで・・・)

また、INSTALL.mdにあるシェルスクリプトは適当に書きなぐっただけでテストもしてないので絶対にそのまま実行しないでください。途中でエラーで止まって地獄をみます。

では、楽しいLinuxライフを。

Xamarin.iOSにおける循環参照

この記事は[初心者さん・学生さん大歓迎!] Xamarin その1 Advent Calendar 2017の12日目の記事です。
昨日は@kmz_kappaさんでXamarinでも難読化のすゝめでした。

今回はXamarin.iOSをやる上で気をつけたほうがいいと思ったメモリリークのお話です。
基本的にはXamarin.iOS特有のお話でXamarin.FormsやXamarin.Androidは大丈夫なはず。。。
ただし、Xamarin.FormsでCustomRenderer等を用いてNSObjectを触る場合は注意が必要です。

また、もし書いてあることに誤りがあったときはご指摘をよろしくお願いします🙇

環境

循環参照とは

循環参照とは、以下のコードのようにHogeというオブジェクトとFooというオブジェクトがお互いに参照を持ってしまっている状態です。

class Hoge 
{
    Foo foo;

    public Hoge() 
    {
       foo = new Foo(this);
    }
}

class Foo 
{
    Hoge hoge;

    public Foo(Hoge hoge) 
    {
        this.hoge = hoge;
    }
}

.NET Frameworkでは特に問題ないコードですが、Xamarin.iOSでは特定条件下でオブジェクトが破棄されなくなる可能性があります。

iOSにおける循環参照問題

.NET Frameworkなどのオブジェクトを追跡して破棄するGCを採用している場合は上記コードでも問題は起きないのですが、代入などでオブジェクトの参照が増えたときにカウントを増やし、0になったら破棄をする参照カウント方式のGCを使っているiOSでは問題が発生します。

たとえば

void NewHoge() 
{
    var hoge = new Hoge();
}

としたとき、hogeの参照カウントは+1されます。 また、コンストラクタ内でFooをHogeのフィールドfooに代入してfooの参照カウントは+1されます。 そしてFooにthisを渡しているのでhogeの参照はまた+1されます。

このとき、上記例でメソッドを抜けた場合hogeは破棄されることを期待します。 が、メソッドを抜けたことでhogeの参照カウントは-1されますが、まだhogeのフィールドfooが持っている参照が残っています。 参照カウント方式では参照カウントが0にならないとオブジェクトは破棄されないので0になっていないhogeは破棄されません。 また、foohogeが参照を持っているため破棄されません。

よって永遠にhogefooの参照カウントは0にならず、NewHoge()というメソッドを抜けたあとも生き続け、メモリリークとなるのです。

Xamarin.iOSにおける循環参照問題

上の例ではすぐに循環参照になりそうですが、Monoは優秀で実は上記のようにただフィールドで循環参照した場合ではきちんとオブジェクトは破棄されます。

しかしNSObjectが関わってきた場合は注意が必要です。 たとえば、

public class Hoge : UIView
{
    public Foo Foo { get; set; }
    public Hoge() : base(CGRect.Empty)
    {
        Foo = new Foo {Hoge = this};
    }
}

public class Foo : UIView
{
    public Hoge Hoge { get; set; }
}

上記コードは先程のとおりオブジェクトが使われなくなったときに破棄されます。

しかし、

public class Hoge : UIView
{
    public Hoge() : base(CGRect.Empty)
    {
        var foo = new Foo {Hoge = this};
        AddSubview(foo);
    }
    ...
}
...

のようにUIViewのメソッドであるAddSubviewに循環参照しているfooオブジェクトを渡すとオブジェクトが破棄されなくなります

これはメソッドにかぎらず、プロパティやイベントに循環参照したオブジェクトを渡しても発生します。

特にイベントなどでラムダ式を使うと、注意していても循環参照するときがあります。

たとえば、

public class ViewController : UIViewController
{
    protected ViewController(IntPtr handle) : base(handle)
    {
        // Note: this .ctor should not contain any initialization logic.
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        // Perform any additional setup after loading the view, typically from a nib.

        var button = new UIButton();
        button.TouchUpInside += (sender, args) =>
        {
            var viewController = this.Storyboard.InstantiateInitialViewController();
            this.NavigationController.PushViewController(viewController, true);

        };

        View.AddSubview(button);
    }
}

上のようなコードは.NETではよく見そうなコードですが、ViewControllerとbuttonが循環参照してオブジェクトが破棄されません。

これはbutton.TouchUpInsideに渡しているラムダ式内でthisを使うことによってViewControllerの参照がキャプチャされbuttonオブジェクトが持ってしまい、循環参照になってしまうのです。

循環参照の解決方法

循環参照はお互いに参照を持っていることが問題なので適切なタイミングで開放するようにすれば問題ありません。

いくつか方法はあるのですが、ここでは2つ書きます。

WeakReference

1つ目の方法は、オブジェクト同士がお互いに強く参照していることが問題なので片方を弱参照にすれば大丈夫です。

public class Hoge : UIView
{
    public Foo Foo { get; set; }

    public Hoge() : base(CGRect.Empty)
    {
        var weakRef = new WeakReference<Hoge>(this);
        Foo = new Foo(weakRef);
        AddSubview(Foo);
    }
}

public class Foo : UIView
{
    WeakReference<Hoge> weakHoge;

    public Hoge Hoge
    {
        get
        {
            if (weakHoge.TryGetTarget(out var hoge))
            {
                return hoge;
            }

            return null;
        }
    }

    public Foo(WeakReference<Hoge> weakHoge) : base(CGRect.Empty)
    {
        this.weakHoge = weakHoge;
    }
}

AddSubViewをしていますが、上記コードは問題なくオブジェクトが破棄されます。

手動で開放

2つ目はオブジェクトが破棄されるタイミングでも循環参照が残っているのが問題なので、自分で開放してあげます。

public class Hoge : UIView
{
    public Foo Foo { get; set; }

    public Hoge() : base(CGRect.Empty)
    {
        Foo = new Foo(this);
        AddSubview(Foo);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Foo.RemoveFromSuperview();
        }
        base.Dispose(disposing);
    }
}

public class Foo : UIView
{
    Hoge hoge;
    public Foo(Hoge hoge) : base(CGRect.Empty)
    {
        this.hoge = hoge;
    }
}

ここで注意なのが、Dispose(bool)メソッドは正しく実装されていた場合オブジェクトが破棄されるタイミングで呼び出されますが、Finalizerからの呼び出しなのでdisposingはfalseになります。 なので上記コードの場合はHogeが必要なくなったタイミングでDispose()メソッドを呼び出して上げる必要があります。

また、イベントの場合、

public class ViewController : UIViewController
{
    protected ViewController(IntPtr handle) : base(handle)
    {
        // Note: this .ctor should not contain any initialization logic.
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        // Perform any additional setup after loading the view, typically from a nib.

        var button = new UIButton();
        button.TouchUpInside += (sender, args) =>
        {
            var viewController = Storyboard.InstantiateInitialViewController();
            NavigationController.PushViewController(viewController, true);
        };

        View.AddSubview(button);
    }
}

のような書き方だとラムダ式なのでイベントの購読を解除できず、またViewDidUnloadは非推奨で使用すべきではないので解除するタイミングがありません。

なので、

public class ViewController : UIViewController
{
    UIButton button;
    
    protected ViewController(IntPtr handle) : base(handle)
    {
        // Note: this .ctor should not contain any initialization logic.
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        // Perform any additional setup after loading the view, typically from a nib.

        button = new UIButton();
        View.AddSubview(button);
    }

    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);
        
        button.TouchUpInside += OnTouchUpInside;
    }

    public override void ViewDidDisappear(bool animated)
    {
        base.ViewDidDisappear(animated);

        button.TouchUpInside -= OnTouchUpInside;
    }

    private void OnTouchUpInside(object sender, EventArgs args)
    {
        var viewController = Storyboard.InstantiateInitialViewController();
        NavigationController.PushViewController(viewController, true);
    }
}

のようにViewWillAppearでイベントを購読、ViewDidDisappearでイベント購読解除すれば大丈夫です。

循環参照の調べ方

Xamarin Profilerが便利そうなのですが、Enterpriseライセンスを持ってないので詳細はわかりません。

Xamarin Profiler について - Xamarin 日本語情報

一番簡単な方法は、NSObjectならDispose(bool)メソッドをoverrideしてConsole.WriteLine()なりなんなりする方法です。

循環参照をするとオブジェクトが破棄されなくなるのでFinalizerから呼び出されるDispose(bool)メソッドも呼び出されなくなります。

ただし、iOS Simulatorだと結構頻繁にDisposeメソッドが呼ばれていることが確認できるのですが、iOS実機だとなかなかDisposeメソッドが呼ばれません。

これはiOS SimulatorだとGC.Collect()?が頻繁に呼び出されているからだそうです。
memory management - the garbage collector in Xamarin Ios it not working on devices - Stack Overflow

なので実機ではどこかにGC.Collectを仕込むか、リソースを食う処理を頻繁に起こすか、ひたすら待つかして確認する必要がありそうです。

もっといい方法があれば教えてください。。

まとめ

ここではiOSで初めて開発する人が嵌りそうな循環参照によるメモリリークについて書きました。

C#の世界にいる限り、循環参照によるメモリリークは大丈夫なはずですが、UIView.AddSubviewのようなメソッドやプロパティを通してC#の世界の外に出ると注意しなければメモリリークになってしまいます。

ここいら辺かなりややこしいのでまだまだ罠がありそうですが、

  • 循環しそうなフィールドやプロパティを持つときはWeakRefenrenceを使用する
  • イベントはしっかり購読解除する

などを心がけていれば大丈夫だと思います。

明日は@DevTakasさんです。
よろしくお願いします!

Kotlin/NativeのiOSサンプルを動かしてみる

Kotlin/Nativeが頑張っているっぽいので、iOSのサンプルを実行してみました。

・・・実行しただけです。

環境

ちなみに

私はビルドし終わったあとに気づきましたが、GitHubReleaseにおそらく各種プラットフォーム向けにビルドされたやつがあるっぽいです。。

なのでそこから持ってきた場合はiOSで実行できるようにするまで飛ばせるはずです。
時間が有り余っている人はCloneからお読み下さい。

kotlin-nativeリポジトリのClone

今回はKotlin/Nativeのリポジトリに含まれているsamples/uikitを実行してみたいと思います。 まずはリポジトリをClone。

$ git clone https://github.com/JetBrains/kotlin-native.git

次にkotlin-nativeのディレクトリに移動し、依存関連をダウンロード。

$ cd kotlin-native
$ ./gradlew dependencies:update

ちょっと時間がかかるかもしれません。軽く筋トレしていましょう。

そしてコンパイラや、各種プラットフォームのライブラリをコンパイル

$ ./gradlew bundle

ただし、README.mdに

The build can take about an hour on a Macbook Pro.

と書かれている通り、ビルドに1時間近くかかります(私の環境で46分でした)。 待っている間は筋トレでもしてましょう。

ビルドが終わったらKotlin/Native側の準備は完了です。
さっそくiOSプロジェクトを見ていきましょう!

iOSで実行できるようにする

samples/uikitに移動し、.xcodeprojをオープン。

$ cd samples/uikit
$ open konan.xcodeproj

早速実行、、、といきたいところですが、いくつか修正が必要です。

まず最初にBundle Identifierを変更します。
初期状態だとorg.jetbrains.konanになっているのでここを適当に変更します。

f:id:crocus7724:20171107021121p:plain

私は今回com.example.konanにしました。

また、SigningTeamを自分のに指定します。

次にRunボタンをクリックしてみるとわかるのですが、ビルドが

cp: /~/kotlin-native/samples/uikit/build/konan/bin/app.kexe: No such file or directory

でこけます。

どうやらkotlinのコードをビルドしたとき、成果物がuikit/build/konan/binに吐き出される想定のはずなのですが、ないっぽいです。

実は成果物はuikit/build/konan/bin/iphoneに吐き出されるようなので単純にミスみたいです()

ということで、Build PhasesReplace Binaryを以下のように修正。

cp "$SRCROOT/build/konan/bin/iphone/app.kexe" "$TARGET_BUILD_DIR/$EXECUTABLE_PATH"

f:id:crocus7724:20171107021339p:plain

さあエラーも消えたし実行!!してみるとビルドは通りますが、実行されません。

実はTargetをシミュレータにしていたのですが、どうやらシミュレータ用のバイナリを吐いてくれないらしく、再生ボタンを何回か押すと以下のようなメッセージが表示されます。

f:id:crocus7724:20171107021356p:plain

なので、MacbookiPhoneを繋いで実機ビルドしましょう!

実行ボタンを押すとビルドされ、実機デプロイされるのですが、以下のメッセージが表示され、初回は時間がかかります。

f:id:crocus7724:20171107021417p:plain

筋トレしていましょう。

うまくいけば実行できているはずです!!

f:id:crocus7724:20171107021511j:plain

お疲れ様でした!!

まとめ

仕組みはどうなっているのかなーと思ったのですが、.xcodeprojのBuild Phasesを見た感じ、

  • kotlinコードをkonanc?を使ってネイティブコンパイル
  • 成果物を$EXECUTABLE_PATH(konan.app/konan)にReplace

しているっぽいですね。

なのでobjcコードは完全にダミーでIBホニャララするくらいしか使わない感じ?

あと、現状唯一Kotlin/Nativeに対応しているIDEがCLionなのですが、開いてみた感じまだ対応されていないので補完なしで頑張るしかないっぽいですね(非常に辛い)。

また、Common moduleがない?(みつからなかった)のでネイティブAPIで頑張るしかないみたいです(非常に辛い)。

現状iOS開発するにはとてもとてもとても辛いですが、将来Common moduleが出たりIDE対応が進んだりすればきっと選択肢の中に一瞬入るようになると期待しています。

Visual Studio for Mac Extensionで独自Padを作る

Visual Studio for Macでの独自Padの実装方法です。 ちなみにPadは以下のようなやつです。間違ってたらごめんなさい。

f:id:crocus7724:20170520015028p:plain

今回はサンプルとしてビルドした回数とデバッグした回数を記録しておくPadを作ります。
(本当はRunした回数を取りたかったけど取得の仕方がわからなかった・・・)

PadContentの作成

まず最初にPadContentを継承したクラスを作成します。これはiOS/AndroidでいうUIViewController/Activityみたいな役割で、表示するViewを組み立てたりします。たぶん

実際にコードを見てみましょう。

using System;
using Gtk;
using MonoDevelop.Components;
using MonoDevelop.Debugger;
using MonoDevelop.Ide;
using MonoDevelop.Ide.Gui;
using MonoDevelop.Projects;

namespace SamplePadExtension
{
    /// <summary>
    /// ビルドした回数とデバッグ実行した回数を表示するPad
    /// </summary>
    public class SamplePadExtensionPad : PadContent
    {
        // デバッグした回数
        private int debugCount;

        // ビルドした回数
        private int buildCount;

        // デバッグした回数を表示するラベル
        private Label debugCountLabel;

        // ビルドした回数を表示するラベル
        private Label buildCountLabel;

        // 実際に表示するものを指定するバッキングフィールド
        // overrideのControlプロパティがget onlyなので
        private Control control;

        /// <summary>
        /// 実際に表示するControl(View)を格納するプロパティ
        /// </summary>
        public override Control Control => control;

        /// <summary>
        /// 実際に表示されるときに呼ばれる?
        /// </summary>
        /// <param name="window">Padのウィンドウ. Toolbarとかとれる</param>
        protected override void Initialize(IPadWindow window)
        {
            buildCountLabel = new Label {Text = $"Build count: {buildCount}"};
            debugCountLabel = new Label {Text = $"Debug count: {debugCount}"};

            // 縦積みするレイアウト
            var vBox = new VBox {buildCountLabel, debugCountLabel};
            // 表示
            vBox.ShowAll();
            control = vBox;

            IdeApp.ProjectOperations.StartBuild += OnStartBuild;
            DebuggingService.DebugSessionStarted += OnDebugSessionStarted;
        }

        // ビルドが始まったときに呼ばれる
        private void OnStartBuild(object sender, BuildEventArgs args)
            => buildCountLabel.Text = $"Build count: {++buildCount}";

        // デバッグ実行が始まったときに呼ばれる
        private void OnDebugSessionStarted(object sender, EventArgs eventArgs)
            => debugCountLabel.Text = $"Debug count: {++debugCount}";

        public override void Dispose()
        {
            IdeApp.ProjectOperations.StartBuild -= OnStartBuild;
            DebuggingService.DebugSessionStarted -= OnDebugSessionStarted;

            base.Dispose();
        }
    }
}

注意点なのですが、デバッグの開始を取得するためにMonodevelop.Debuggerを使っています。
デフォルトだと参照できないのでVisual Studio for Mac ExtensionプロジェクトのAddin Reference(下の画像の赤枠のやつ)をダブルクリックして表示されるポップアップからMonodevelop.Debuggerを選択してください。

f:id:crocus7724:20170520020303p:plain

Manifest.addin.xmlの編集

次に上で書いたPadContentを継承したクラスをVisual Studio for Mac Extensionが認識できるようにします。 Propertiesフォルダの中にあるManifest.addin.xmlファイルを以下のように編集します。

<?xml version="1.0" encoding="UTF-8"?>
<ExtensionModel>
    <!-- MainMenu→View→PadsにPadを追加する -->
    <Extension path="/MonoDevelop/Ide/Pads">
        <!-- 実際に追加するPad -->
        <!-- idは固有識別子 -->
        <!-- defaultPlacementは表示させたときのデフォルトの位置 -->
        <!-- classはPadContentを継承したクラスの完全修飾名 -->
        <!-- _labelはPadsに追加されたときに表示される文字 -->
        <Pad id="SamplePadExtension.SamplePadExtensionPad"
             defaultPlacement="Bottom"
             class="SamplePadExtension.SamplePadExtensionPad"
             _label="Sample Extension Pad"/>
    </Extension>
</ExtensionModel>

これで準備は完了です!

実行

それでは実際に実行してみましょう。

f:id:crocus7724:20170520021013p:plain

実行すると上のようなのが追加されていて、ビルドしたりデバッグしたりすると下のようにカウントが増えていくはずです。

f:id:crocus7724:20170520021057p:plain

Xamarin StudioまではファイルテンプレートにGtkがあったのですが、VS for MacからGtkテンプレートが消失してしまい、Xamarin StudioはVS for Macに置き換わる予定ですからGtkファイル手作業作成しか残されてないかなーと思うのですが辛すぎるので手抜きViewです。
もし凝ったレイアウトを作成したい場合は覚悟してください私はやりたくない

もうちょっと詳しく

もう少し凝ったレイアウトやよくあるリスト形式のPadを作成したい場合はおそらくBreakpointのPad実装である

monodevelop/BreakpointPad.cs at master · mono/monodevelop · GitHub

が参考になると思います。

まとめ

Visual Studio for Mac ExtensionならPadの作成も見た目を気にしなくていいならとても簡単にできます。
Padが作成できるとやれることの幅が広がるのでぜひ作ってみてください。

Visual Studio for Mac Extensionの作り方

Visual Studio for Macが正式リリースされたっぽい?のでVisual Studio for Macの拡張の作り方を調べてみました。(Xamarin Studio Addinと変わってなかったけど)

Addin Makerインストー

最初にVS for Mac ExtensionのテンプレートやデバッグサポートをしてくれるAddin Makerを入れます。

VS for Macを開き、Main MenuのVisual Studio→Extensions…を選択します。

f:id:crocus7724:20170513161535p:plain

そして開いたウィンドウのGallery→Addin Development→Addin Makerを選択しインストールします。

f:id:crocus7724:20170513161923p:plain

いつものようにFile→New Solution…を開き、Other→Miscellaneous→Generalに以下のようなものが追加されていたらインストール成功です。

f:id:crocus7724:20170513162226p:plain

_人人人人人人人人人人人人人人_
> Xamarin Studio Addin <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

Extension作成

ここではサンプルとしてメニューから選択するとConsole.WriteLine("Hello World");を挿入するExtensionを作りたいと思います。(誰得だけど)

まずNew SolutionでXamarin Studio Addinを選択し、プロジェクトを作成します。
なおAddin Makerをインストールして最初の数回はよくアセンブリが見つからなかったりするエラーが発生しますが、VS for Macを再起動したりビルドしたりするとそのうちなおります。

次にメニュー等から選択されたときに実行されるCommandHandlerを作成し以下のように記述します。

using MonoDevelop.Components.Commands;
using MonoDevelop.Ide;

namespace SampleVSExtension
{
    /// <summary>
    /// コマンドが選択されると現在のキャレット位置に
    /// <code>Console.WriteLine("Hello World");</code>が挿入される
    /// コマンドハンドラー
    /// </summary>
    public class HelloWorldCommandHandler : CommandHandler
    {
        /// <summary>
        /// メインメニューなどが開かれるときなどに実行される
        /// ここで可視性や非活性、動的な文字の変更などができる
        /// </summary>
        /// <param name="info">コマンドのステータス</param>
        protected override void Update(CommandInfo info)
        {
            // ActiveDocument(現在開いているファイル)がnullではない(なにかしらのファイルを開いている)
            // ときにコマンドを表示
            info.Visible = IdeApp.Workbench.ActiveDocument != null;
        }

        /// <summary>
        /// コマンドが実際に選択されたときに実行される
        /// </summary>
        protected override void Run()
        {
            // 現在開いているエディタの情報を取得
            var editor = IdeApp.Workbench.ActiveDocument.Editor;
            // 現在のキャレット位置にConsole.WriteLine("Hello World");
            // を表示
            editor.InsertAtCaret("Console.WriteLine(\"Hello World\");");
        }
    }
}

もしusing MonoDevelopとかでエラーが表示されたらVS for Macを一回閉じてもう一度起動してみてください。

細かい説明などはソースコードに書いたので省略して次にCommandの種別を決めるSampleVSExtensionCommandsを作成します。

namespace SampleVSExtension
{
    public enum SampleVSExtensionCommands
    {
        InsertHelloWorld,
    }
}

これはただ単に種別を表すだけなのでとくに説明はありません。

最後に実際にどのような拡張をするかをVS for Macに知らせるManifest.addin.xmlを編集しましょう。
Manifest.addin.xmlAssemblyInfo.csなどがあるPropertiesフォルダの中にあります。

以下のように定義してみましょう。

<?xml version="1.0" encoding="UTF-8"?>
<ExtensionModel>
    <!-- Editという種類のコマンドを定義する -->
    <Extension path="/MonoDevelop/Ide/Commands/Edit">
        <!-- idは用意した種別を表すenumを完全修飾名({namespace}.{enum}.{value})で指定 -->
        <!--_labelはコマンドが表示される文字 -->
        <!-- defaultHandlerはCommandHandlerを拡張したクラスを完全修飾名で指定 -->
        <Command id="SampleVSExtension.SampleVSExtensionCommands.InsertHelloWorld"
                 _label="Insert Hello World"
                 defaultHandler="SampleVSExtension.HelloWorldCommandHandler"/>
    </Extension>
    <!-- MainMenuのEditに表示するコマンドを定義する -->
    <Extension path="/MonoDevelop/Ide/MainMenu/Edit">
        <!-- 上で定義したコマンドのidを指定 -->
        <CommandItem id="SampleVSExtension.SampleVSExtensionCommands.InsertHelloWorld"/>
    </Extension>
</ExtensionModel>

これで準備は完了です。実際に実行してみましょう!

MainMenuのEditにInsert Hello Worldが追加されていて

f:id:crocus7724:20170513171210p:plain

選択したら現在の行に挿入されたら成功です!

f:id:crocus7724:20170513171321p:plain

何故かキャレット位置ではなく行の頭に挿入されているのが謎というかバグですが気にしたら負けです。これで無事やりたいことはできました!!

他の機能の作り方

Visual Studio for Mac Extensionは参考になるものがたくさん公開されています。

私の場合はEditなどのソースコードを編集する機能などは以下を参考にしましたし、

monodevelop/main/src/addins/CSharpBinding at master · mono/monodevelop · GitHub

プロジェクトテンプレートなどの作り方は以下が参考になりました。

monodevelop/main/src/addins/AspNet at master · mono/monodevelop · GitHub

そしてこれらは以下にまとまっています。

monodevelop/main/src/addins at master · mono/monodevelop · GitHub

素晴らしいですね(((

まとめ

VS for MacのPreviewが外れ、.NET CoreやC#7対応を含めてXamarin Studioから徐々に移行してくると思います。
VS for MacのExtensionもXamarin Studio addinとあまり変わってない(たぶん)と思うのでぜひ作ってみてください!!

RiderでXamarin.Androidがサポートされました!!

RiderのPublic EAP20でXamarin.Androidがサポートされました 🎉🎉🎉🎉🎉
(ついでにT4テンプレートとNode.js)

さっそく試してみましょう。

Androidプロジェクト作成

Riderが起動したらNew solution or projectを選択します。

f:id:crocus7724:20170415001848p:plain

Bindings Libraryプロジェクトまである・・・!!
ネイティブバインディングまでRiderだけでできるかはあとで調べるとして、とりあえずBlank Appを選択してみます。

f:id:crocus7724:20170415002343p:plain

初期状態はこんな感じ。

さっそくデバッグしてみます。

f:id:crocus7724:20170415002535p:plain

シミュレータを適当に選んで・・・

f:id:crocus7724:20170415002638p:plain

画面真っ黒(´・ω・`)
無事に実行できました!!

f:id:crocus7724:20170415003555p:plain

ちなみにブレークポイントでもちゃんと止まります。

f:id:crocus7724:20170415014234p:plain また、Xamarin.FormsのAndroidプロジェクトも実行できました!!
(Xamarin.iOSがloadしてある状態だとビルドエラーになったのでわざとunloadしてます)

これは捗りますね!!

注意点

ただRider自体がまだPublic EAPな上にXamarin.Androidに対応したばっかりなので結構怪しいところがあります。

Riderで作成したXamarin.AndroidプロジェクトがXamarin Studioで開けない

開けません。

f:id:crocus7724:20170415005225p:plain

ただエラー内容はただ単にXamarin StudioはそんなProject Type GUIDなんて知らないよって言っているだけなので書き換えればOKです。

Xamarin.Androidの.csprojの

<ProjectTypeGuids>{02CD1F06-00EA-420C-944B-AD8BE8AD9848}</ProjectTypeGuids>

<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

にすれば

f:id:crocus7724:20170415005627p:plain

Xamarin Studioでも開けました!!

ただ個人的にはまだXamarin Studioで作成してからRiderで開く運用のほうがいいと思います。(書き換えよろしくないと思いますし)

C#7使えない?

RiderはC#7に対応しているのですが、Xamarin Studioで作成したプロジェクトだと残念ながらコンパイル時にエラーになってしまいます。

ただ、Visual Studio for Macで作成したプロジェクトはRiderでもC#7で実行できるので、ヒトバシラーにはおすすめです() f:id:crocus7724:20170415022758p:plain




今回のバージョンアップでRiderでもXamarin.Androidプロジェクトが実行できるようになりました!
今回は適当なサンプルプロジェクトを実行しただけでこれが実用に耐えうるかは未知数ですが、これは大きな前進だと思います。また、Xamarin.iOSに関しても

ということで期待ですね・・・!!