2024年まとめ
気づけば2024年も今日で終わりです。
今年で29歳だった私にとって2024年は色んな意味で変化のあった年でした。
来年で30歳になって記憶力が衰える一方なので今のうちから1年のまとめを残しておくべく、すっかり放置されたこのブログを更新しようと思います。
なお思いつきなので来年以降も更新されるかは謎。
1年のできごと
コンタクトをつけるようになった
後述の用事でコンタクトが欲しくなったので眼科で処方箋をもらいに行きました。
実は前もコンタクトを処方してもらいに眼科に行ったことがあるのですがその時は1時間粘ってもつけられるようにならず、心が折れて泣きながら帰ったのでリベンジでした。
結果は1.5時間粘って成功! 🎉
無事に処方箋をもらえました。
なおまたしても心が折れそうな中、看護師?のお姉さんに「がんばれ!がんばれ!」と成人してから初めてレベルの応援をされました。
あれがないとまたしても泣きながら帰ってたかもしれません。お姉さんありがとう。
スノボ
フリーランスの友人が平日からスノボに行って楽しそうなので連れて行ってもらいました。(日帰り)(私は有給)
場所は新潟のどこか(忘れたけど群馬寄り(?))。
このためにコンタクトをつけれるようにしました。
私はウィンタースポーツ初体験なくらいには完全に初心者だったのですが、初心者的なエリアで30分くらい滑って全然滑れなかった私に業を煮やした友人が普通に滑るような人がいるエリアに連れて行き放置プレイを食らいました。
結果、2時間かけてなんとか下まで着いた私が疲れ果てて終了。
滑れるようにはなりませんでした。
こう書くとなかなかひどい結果ですが、初めてのウィンタースポーツで滑れないなりに楽しめました。
敗因は圧倒的運動不足だったので体力をつけていつかリベンジしたいです。
あと滑ったあとの温泉は最高。
PC が壊れた
いい出来事ではないのですが、 PC が壊れました。
元々2020年に自作したやつでスペックは以下 👇
なのですが、去年引っ越してから調子がちょっとずつ悪くなっていき、最近は BIOS すら起動しなくなりました。 原因はなんとなくわかっているものの、治すのがめんどく放置しています。
ただ来年のモンハンワイルズはやってみたいので BTO とかで新しい PC を買おうか悩み中(自作する元気はもうない)。
転職した
2020年10月に入社した会社を2024年8月をもって退職しました。
転職しようと思った理由は人間関係とか社内制度への不満とかありきたりですが、転職を決意したのは面白そうなお仕事だと思った会社の内定が貰えたことです。
どうしても隣の芝生は青く見えてしまう。
実際後述する理由で転職をして良かったと思いました。
前職もいいところだったけどね!
旅行(part1)
滅多に旅行しない私ですがときたま友人と遠出をしていて、今回は宮城に行ってきました。(日帰り)
私は神奈川に住んでいて東北の宮城というと結構離れている印象があるのですが、今の時代東京駅から 1.5時間から2時間弱で仙台駅まで着いちゃうんですね。すごい。
海鮮と牛タンを食べに行ったのですが、海鮮が美味しくて量を食べたのと食べる時間が遅かったせいもあり牛タンを食べるのが辛かった。美味しかったけど。
今度はちゃんと時間配分を考えて行きたいですね。
あと仙台うみの杜水族館にも行きました。
色々なお魚見れてよかった。
🐈
ネコを飼いたいと8年くらい言い続けて、やっと今年飼い始めました。
はーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー pic.twitter.com/APNBz0J32q
— k.yamamoto🐈 (@kymmt24) August 27, 2024
品種は私がいいなーと持っていたベンガル。
なおこのベンガルちゃん、引き取った当初は耳ダニ・ネコ回虫・風邪・気象由来の体調不良のコンボが重なりご飯を食べられないくらい弱ってしまっていました(元々のところが悪いというよりは、引っ越しのストレスの影響が大きそう)。
なので引き取って1ヶ月くらいは1週間に1〜2回動物病院に通っていました。
転職したことによりほぼ在宅になったのと、平日昼間でも中抜けできるようになったので(前職もできたけど申請がめんどい)通院が楽なのはとても助かりました。
ありがとう弊社。
今では避妊手術も終え、めっちゃ元気になりました。
ネコがいることでだいぶ縛りも増えたけど、何よりかわいいので無問題。
旅行(part2)
親が還暦になるので記念(?)に三重県に旅行に行ってきました。(2泊3日)
当日に東京駅待ち合わせのはずが色々あって集合に失敗し、私ひとりだけで新幹線に乗るなどのアクシデントがあったものの、ご飯が美味しかったりいい景色が見れたりと個人的に満足。
旅行だけど東京駅での集合に失敗したので無事ソログリーン車1人旅
— k.yamamoto🐈 (@kymmt24) November 22, 2024
なお伊勢神宮にも行ったのですが、ちょうど新嘗祭の時期だったらしく人が凄かったです。
伊勢神宮にお参りだけするなら新嘗祭の時期は外した方がよさそう。
📸
前々から気になってはいたのと、ネコの記録を残しておきたいと思いカメラを買いました。
my new gear... pic.twitter.com/IFnrL0DJ7U
— k.yamamoto🐈 (@kymmt24) December 5, 2024
機種は α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に移行してみました。
その環境がようやく落ち着いてきたので備忘録代わりに。
環境
Ubuntu17.10はBIOSを破壊する致命的な不具合がありましたが、幸いにもMacBookProだったので免れた。
主にインストールしたアプリは以下。
i3-gaps
いくつかのLinux記事で気になっていたタイル型のウィンドウマネージャであるi3、の改良バージョンであるi3-gapsを使っています。
タイル型ウィンドウマネージャはいくつかありますが、最近の記事は大体i3という印象だったのでi3、その中でもウィンドウ間の隙間を開けられるほうが個人的に好きなのでi3-gapsを選びました。
ウィンドウが重ならないので便利ですが世の中がウィンドウが重なる前提なのでたまにバグっぽい挙動をして辛い。
Compton
コンポジット型のウィンドウマネージャ、らしい。
i3のデフォルトだとウィンドウの透過ができなくて透け透け好き派としては微妙だったので導入しました。
アプリケーション毎にウィンドウの透過設定を変えられてめっちゃ便利です。
↑が導入前
↑導入後
(Google Cromeを透けさせたらさすがに見づらかったので透過設定をしてない) 透け透け好きには最高。
Polybar
トップバー。i3にはデフォルトでバーは付いてくるのですが、こっちのほうが自由度が高い?らしいので導入。
シェルスクリプトとか使えるらしいですがまだ良くわかってないのでこれで放置。 どんなに充電しても95%止まりだったりボリュームが明らかにずれていても放置。
rofi
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
マルチプラットフォームなマークダウンエディタ。主にメモ用途に使っています。
2018年1月現在はβ版でその間は無料で使えるらしい?です。
テキスト+プレビューではなく、書いていくそばから変換されていく形式なので横幅をあまり使いたくない自分には便利。あとcssで見た目を変えられるので周りの色に合わせた感じにしています。
また、rofiと合わせて新規作成したりメモったファイルを開けるようにしています。
まとめ
最近「Linuxええで」という声をたまに聞くので思い切って移行してみたのですが、最大の学びは「WindowsやmacOSはすごかった」でした。
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
は破棄されません。
また、foo
もhoge
が参照を持っているため破棄されません。
よって永遠にhoge
とfoo
の参照カウントは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のサンプルを実行してみました。
・・・実行しただけです。
環境
- Macbook Pro(Mid 2015)
- Core i7, Memory 16GB
- macOS High Sierra
- Xcode 9.1
- kotlin/native 0.4
ちなみに
私はビルドし終わったあとに気づきましたが、GitHubのReleaseにおそらく各種プラットフォーム向けにビルドされたやつがあるっぽいです。。
なのでそこから持ってきた場合は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
になっているのでここを適当に変更します。
私は今回com.example.konan
にしました。
また、Signing
のTeam
を自分のに指定します。
次に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 Phases
のReplace Binary
を以下のように修正。
cp "$SRCROOT/build/konan/bin/iphone/app.kexe" "$TARGET_BUILD_DIR/$EXECUTABLE_PATH"
さあエラーも消えたし実行!!してみるとビルドは通りますが、実行されません。
実はTargetをシミュレータにしていたのですが、どうやらシミュレータ用のバイナリを吐いてくれないらしく、再生ボタンを何回か押すと以下のようなメッセージが表示されます。
なので、MacbookにiPhoneを繋いで実機ビルドしましょう!
実行ボタンを押すとビルドされ、実機デプロイされるのですが、以下のメッセージが表示され、初回は時間がかかります。
筋トレしていましょう。
うまくいけば実行できているはずです!!
お疲れ様でした!!
まとめ
仕組みはどうなっているのかなーと思ったのですが、.xcodeprojのBuild Phasesを見た感じ、
しているっぽいですね。
なので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は以下のようなやつです。間違ってたらごめんなさい。
今回はサンプルとしてビルドした回数とデバッグした回数を記録しておく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を選択してください。
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>
これで準備は完了です!
実行
それでは実際に実行してみましょう。
実行すると上のようなのが追加されていて、ビルドしたりデバッグしたりすると下のようにカウントが増えていくはずです。
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…を選択します。
そして開いたウィンドウのGallery→Addin Development→Addin Makerを選択しインストールします。
いつものようにFile→New Solution…を開き、Other→Miscellaneous→Generalに以下のようなものが追加されていたらインストール成功です。
_人人人人人人人人人人人人人人_
> 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.xml
はAssemblyInfo.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が追加されていて
選択したら現在の行に挿入されたら成功です!
何故かキャレット位置ではなく行の頭に挿入されているのが謎というかバグですが気にしたら負けです。これで無事やりたいことはできました!!
他の機能の作り方
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)
New Rider build: Xamarin Android, Node.js, Resx and T4, search in Alt+Enter, improved .NET Core and NuGet support: https://t.co/5TRdGdIrip pic.twitter.com/80c1E4DZ7u
— JetBrains Rider (@JetBrainsRider) 2017年4月14日
さっそく試してみましょう。
Androidプロジェクト作成
Riderが起動したらNew solution or projectを選択します。
Bindings Libraryプロジェクトまである・・・!!
ネイティブバインディングまでRiderだけでできるかはあとで調べるとして、とりあえずBlank Appを選択してみます。
初期状態はこんな感じ。
さっそくデバッグしてみます。
シミュレータを適当に選んで・・・
画面真っ黒(´・ω・`)
無事に実行できました!!
ちなみにブレークポイントでもちゃんと止まります。
また、Xamarin.FormsのAndroidプロジェクトも実行できました!!
(Xamarin.iOSがloadしてある状態だとビルドエラーになったのでわざとunloadしてます)
これは捗りますね!!
注意点
ただRider自体がまだPublic EAPな上にXamarin.Androidに対応したばっかりなので結構怪しいところがあります。
Riderで作成したXamarin.AndroidプロジェクトがXamarin Studioで開けない
開けません。
ただエラー内容はただ単に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>
にすれば
Xamarin Studioでも開けました!!
ただ個人的にはまだXamarin Studioで作成してからRiderで開く運用のほうがいいと思います。(書き換えよろしくないと思いますし)
C#7使えない?
RiderはC#7に対応しているのですが、Xamarin Studioで作成したプロジェクトだと残念ながらコンパイル時にエラーになってしまいます。
ただ、Visual Studio for Macで作成したプロジェクトはRiderでもC#7で実行できるので、ヒトバシラーにはおすすめです()
今回のバージョンアップでRiderでもXamarin.Androidプロジェクトが実行できるようになりました!
今回は適当なサンプルプロジェクトを実行しただけでこれが実用に耐えうるかは未知数ですが、これは大きな前進だと思います。また、Xamarin.iOSに関しても
@moshkin_aleks Hopefully next month.
— JetBrains Rider (@JetBrainsRider) 2017年4月14日
ということで期待ですね・・・!!