GUIとかも直して一通りできた。あとはデバッグだな。
他にもいろいろ直したいところや実装したい機能があるが、まずは日常的に問題なく使えるようにしたい。
普段Macでぴあきゃす見てるからMacで使うのがいいんだが、MonoDevelopでのデバッグ方法がよくわからん……。
PeerCastStationでGUIを作ってたんだがMonoで動かそうとしたらほんといろいろと問題出てこまる。
GUIはWindows.FormsでMonoでも.NETでもそのまま動くぜーというつもりで作ってた。NotifyIconがなんか上手くうごかないのは以前書いた通り。
.NETでビルドしたバイナリは普通に動くんだけどMonoでビルドしたバイナリはなんかアイコンを作るところ辺りで例外投げられてしまって動かない。Windowsにもっていっても動かない。アイコンが悪いのかとしらべたが256x256サイズのアイコンを含んでいるとビルド時にぶっこわれるようだ。困ったね。困ったけどどうしようもないので256x256のアイコンは外した。
DropDownList化したComboBoxにバインドした物がおかしい。自作のIContentReader型のリストをDataSourceに入れてNameプロパティを表示、あとでどれが選択されるのかなーというのを取得したかっただけなんだが、クリックしてリストを開いた瞬間に内部で型変換に失敗したとかいって落ちちゃうよ。.NETでは普通に動くし、全く同じ方法で別なComboBoxにバインドしたIYellowPageClientは普通に動く。しかしそれがIContentReaderになるとだめだ。
理由はよくわからなかったんだが、どうもIContentReaderのNameプロパティをDisplayMemberで参照してるのが悪いようだ。IContentReaderの参照を保持するだけのContentReaderWrapperというクラスを作ってToStringでIContentReader.Nameを返すだけにして、ContentReaderWrapperのリストをDataSourceにつっこんだら動くようになった。この方法で特に困ることはなかったんでよかったんだけど原因がよくわからないのは気持ちわるいなぁ。詳しく追ってないのでわからないのは当然なんだが。
Windows.Formsはちょっと限界あるかもしれん……。 じゃあGtk#にしようとしたらMac版のMonoDevelopがぽろぽろ落ちまくるのでMacで開発するのは難しそうだ。さらに言えばMac版のGtk+は日本語入力できないので、MonoDevelopでも日本語入力できなくて厳しい。いつか出来るようになってくれるんだろうか。
他のGUIライブラリも検討してみる。wxWidgetsのCLRバインディングはwx.NETはもうメンテされてなさげ。バイナリ落としてきてサンプル実行してみたら落ちたのでなんかダメなんだろう。自前でビルドするのはきっと可能だがめんどい。
QtのCLRバインディングであるところのQyotoはQtの他言語バインディングの例に漏れずLinux用のバイナリしかない。QtRubyの経験からいくと自分でWindows用バイナリをビルドするのは不可能に近い難しさだろう。
IKVM.NET経由でSwingとか。ちょっと頑張りすぎじゃないですかね。
やはりWindows.Formsが頑張れば最低限使えるという点では無難なのか……。次点でGtk#。Windowsでは追加のインストールが必要なのとMacでの日本語入力問題さえクリアできればな。いや次点と言うにはほんとにWindowsでちゃんと動くかどうか確認してないのが不安か。
Rubyのファイナライザは使いづらい。
Ruby使う人にはファイナライザってなんじゃろてことも多いと思うので念の為書いておくと、オブジェクトがGCでぶっころされる時に呼ばれる後始末関数。 File#closeを明示的に呼ばなかった場合とかでもいつかは閉じないときっと困るのでファイナライザで閉じてあげたりしてます。
今回はOpenGLをRubyから呼んだりしてるんだけど、OpenGLのバッファオブジェクトとかを作ったらこいつはGCされないので(ハンドルはただの整数値)どこかで明示的に解放してあげなきゃいけないということでファイナライザを使いたい。
RubyのファイナライザはObjectSpace.define_finalizerに対象のオブジェクトとリソース解放用のprocを渡すと登録できるとのこと。対象オブジェクトがGCされる時に登録しといたprocが呼ばれると。簡単じゃねーか。
しかし普通にprocをその場で作って渡すとprocがファイナライザ対象オブジェクトの参照を保持しちゃって、procはファイナライザ群に登録されているのでGCされず、ファイナライザ対象オブジェクトの参照も消えず、いつまでたっても対象オブジェクトがGCされないという仕組み。そりゃこまった。
class Foo def initialize @data = 'release me!' ObjectSpace.define_finalizer(self, proc { puts @data }) end end
こんな感じ。procがselfを暗黙に保持しちゃうので*1Fooのインスタンスはいつまでたっても解放されない*2。
るりまを読むとTempfileなんかはクラスメソッドでファイナライザ用のprocを作成して渡すとかやるようだ。そうするとprocがインスタンスを保持しないので大丈夫と。しかしめんどい。
class Foo def self.release_proc(data) proc { puts data } end def initialize @data = 'release me!' ObjectSpace.define_finalizer(self, self.class.release_proc(@data)) end end
めんどいと思ったがそれほどでもないかもしんない。procを作るだけのメソッドってのがなんか気持ち悪いが仕方ない……。 いやでもやっぱり気持ち悪い。
ほんとにTempfileはこんなことしてるのかと思って確認したら全然違っていた。
class Foo class ReleaseData def initialize(data) @data = data end def call(obj_id) puts @data end end def initialize @data = 'release me!' ObjectSpace.define_finalizer(self, ReleaseData.new(@data)) end end
こんな感じ。あれー、procはどこいったー?? define_finalizerにはProcだけじゃなくてcallに反応するオブジェクトならなんでも渡せるのでした。ああ、そう……。
これならまあ見た目的にも気をつけなきゃいけない度的にも良さげかな。 ReleaseDataにself持たせるとかやらないことだけ気をつければいい。これでいこう。
ちなみに拡張ライブラリだとオブジェクトのメモリ解放関数でいろいろ解放してやればいいので(C的に)自然に使える。
.NETではファイナライザで自分自身のメンバ変数を読めるので結構簡単。 参照型変数は参照先が既にGCされてる可能性があるんで使えないってとこだけは気をつけないとね。