RubyCocoa 今日のコミット 2014-02-01
やっぱイベントで内職ははかどる。。いや、話もちゃんと聞いてましたよ。
ruby-2.0|2.1まわりの作業。未コミットの修正含めれば、ruby-2.0, 2.1でもテストはだいぶ通るようになってきた。
- "ruby/st.h"の判定をHAVE_RUBY_RUBY_HからHAVE_RUBY_ST_Hに変更。 (r2515)
- ruby/objc/oc_import.rbのうち擬似オープンクラスの機能を別ファイルに分離、ruby-1.8のときのみ有効にするようにした。 (r2516)
- ruby側のOSX::NSNumberに#to_iはあるものの#to_intがなくてうまくいかない箇所があったのでNSNumber#to_intを追加。 (r2517)
- Hash#fetchのエラーがIndexErrorからKeyErrorになっててテストが失敗してたので対応。(r2518)
- テストコードのRuby側で定義したObjective-Cのサブクラスの参照がおかしかったので直した。擬似オープンクラスの仕組みでなんとなく動いてたもの。(r2519)
ruby-2.x対応で、現在目に見えてる課題はこのくらい。
擬似オープンクラス(と今日から呼ぶことにした)をどうするか
たとえば次のようなコードがあったとき、まだNSArrayがruby側に呼び出されていないと、単なるrubyのクラス定義になってしまう。
class NSArray : end
これが定義済みのクラスの追加コードであるように振る舞うために、RubyCocoaではけっこうダーティなことをしちゃってる(「OSX.__rebind_umethod__ ってなに?」参照)んだけど、その機能がruby-2.0以降では動かないっぽい。
そもそも今動いてるのもちょっとあやしいので、2.0では該当機能は廃止する予定(てかr2516がその変更)。でも、「使う前に`OSX.ns_import :ClassName`しろ」ってのもコクな話だと思うので、できるだけ表面上は維持できるようにするつもり。
いちばん簡単な方法は、
- フレームワークやライブラリがロードされたときに、そこに含まれるクラスを自動的にns_importする
なわけだけど、どうやって実装しようか。
たとえば、BridgeSupportファイルの読み込み時にクラスのエントリから実行させるなら、こんな簡単な修正でもruby-2.1で多くのテストが通るようになる。
diff --git a/framework/src/objc/BridgeSupport.m b/framework/src/objc/BridgeSupport.m index 0e88b89..d24b544 100644 --- a/framework/src/objc/BridgeSupport.m +++ b/framework/src/objc/BridgeSupport.m @@ -1606,6 +1606,8 @@ osx_load_bridge_support_file (VALUE mOSX, VALUE path) klass->instance_methods = st_init_strtable(); st_insert(bsClasses, (st_data_t)class_name, (st_data_t)klass); + // reveal Objective-C class to Ruby + rb_funcall(mOSX, rb_intern("ns_import"), 1, rb_str_new2(class_name)); } } break; diff --git a/framework/src/ruby/osx/objc/oc_import.rb b/framework/src/ruby/osx/objc/oc_import.rb index 5a86c9e..c4bec4a 100644 --- a/framework/src/ruby/osx/objc/oc_import.rb +++ b/framework/src/ruby/osx/objc/oc_import.rb @@ -224,10 +224,6 @@ module OSX end end - # Load the foundation frameworks. - OSX.load_bridge_support_signatures('CoreFoundation') - OSX.load_bridge_support_signatures('Foundation') - # create Ruby's class for Cocoa class, # then define Constant under module 'OSX'. def ns_import(sym) @@ -715,5 +711,10 @@ module OSX end + # Load the foundation frameworks. + OSX.load_bridge_support_signatures('CoreFoundation') + OSX.load_bridge_support_signatures('Foundation') + ns_import :NSEnumerator + end # module OSX
ただ、BridgeSupportファイルには定義されていないクラスもあるようで、この修正でNSEnumeratorをns_importしているのもそういう理由。osx/objc/oc_attachments.rbでNSEnumeratorにメソッド追加してるので、その前にns_importしてやんなくちゃいけない。
フレームワークやライブラリのロード時にランタイムが知ってるクラスをぐるぐるとなめて、新しいクラスがあればns_importするって方法くらいしか思いつかない。たぶんパフォーマンス的にはさほど問題ないんだけど、なんかアホっぽい実装に感じる。もうちょっとスマートに実現できないかなあ。。
文字列のエンコーディング
rubyも文字列にエンコーディングを保持できるようになったんだけど、まだ考えてない。
FoundationのNSStringとrubyのStringのエンコーディングの変換表みたいのをつくって、オブジェクト変換時にそれを参照するって感じですかねえ。