きりかノート 3冊め

おあそびプログラミング

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のエンコーディングの変換表みたいのをつくって、オブジェクト変換時にそれを参照するって感じですかねえ。

ArgumentError: invalid value for Integer()

Stringを食わせてrb_Integer(str)してるとこがいくつかあるんだけど、そこでエラーになるっぽい。よくわからんのだけど、エラー箇所はだいたい押さえたので調べれば解決しそう。

(2014-06-06 追記:解決した。

tc_nsarray.rbを流すと落ちる

なんだろね。まだ調べてない。

(2014-06-07 追記:解決した。