きりかノート 3冊め

おあそびプログラミング

RubyCocoa の new を考える

RubyKaigi 自体の日記は書くの時間かかりそうなので、とりあえずこの件を先に。藤本さんのセッション中に irc でちょろっと出ていたけれど、RubyCocoaCocoa クラスにはインスタンスを生成する new メソッドがない。というかエラー。

 % irb -r osx/cocoa
irb(main):001:0> OSX::NSURL.new
RuntimeError: use 'alloc.initXXX' to instantiate Cocoa Object
from /Library/Frameworks/RubyCocoa.framework/Versions/A/Resources/ruby/o
sx/objc/oc_import.rb:251:in `new'
from (irb):1

これは RubyCocoa オブジェクトの初期化の手続きや、initXXX のそれぞれに対応して Object#initialize() を調整するのが手間がかかるってあたりが理由(だと思う)。

でも Ruby なら new したいよな。ただでさえ Cocoa のメッセージ名長いし。で、考えてみたのだけれど、欲しいところから順に CocoaClass.new を定義していけばよいんじゃなかろうか。正月にリファレンスマニュアルの作業してて、クラスメソッド new を定義してあるのを見たときは驚いたけど、定義したからって問題があるわけじゃない。

Hash をキーワード引数的に受け取るのがいいのかな。たとえば、

 class OSX::NSView
# - :frame : ビューの位置、大きさを NSRect で
def self.new(args = {})
args[:frame] ||= NSMakeRect(...) # 省略されたら適当な初期値を
self.alloc.initWithFrame(args[:frame])
end
end

class OSX::NSWindow
# - :contentRect
# - :styleMask
# - :bufferingType
# - :defer
# - :screen
def self.new(args = {})
args[:contentRect] ||= NSMakeRect(...)
args[:styleMask] ||= NSTitledWindowMask & NSClosableWindowMask &
NSMiniaturizableWindowMask
args[:bufferingType] ||= NSBackingStoreBuffered
args[:defer] ||= true
args[:screen] ||= nil # メインスクリーン
self.alloc.initWithContentRect_styleMask_backing_defer_screen(...)
end
end

こんな感じ。適切な初期値を考えるのがちょっと大変かもしれないけれど、引数の多い初期化メソッドが少なくない Cocoa では有効かもしれない。初期値が気に入らなければインスタンスを生成した後で、setter で値を変更すればいいだけだし。