Watson's Blog

RubyMotion の Class について

| Comments

この記事は RubyMotion Advent Calendar 2012 の 20 日目の記事です。

どのような記事を書こうかずいぶん悩んでいたのですが、第一回 RubyMotion 勉強会 の資料からクラスの部分を抜き出して手直ししようと言う手抜きぶりです。

RubyMotion はすべて Objective-C のクラス

CRuby ではルートクラスとして Object というクラスが存在します(Ruby 1.9 からだと BasicObject というのもありますが)。これに対して Objective-C では NSObject というクラスがルートクラスとして存在しています。似たようなクラスがお互いにルートクラスに存在していますね。

RubyMotion では NSObject を Ruby の Object として利用し、ほかのルビーのクラスを構築しています。RubyMotion の REPL で簡単に確認することができます。

1
2
(main)> Object.new
=> #<NSObject:0x937e410>

NSObject をベースとしてクラスを構築しているため、すべてのオブジェクトは何らかの Objective-C のメソッドを持っています。

Objective-C の performSelector で、 Ruby の to_s というメソッドを実行してみました。

1
2
(main)> Object.new.performSelector("to_s")
=> "#<NSObject:0xe13efc0>"

(ちなみに Objective-C ではセレクタを指定する際 @selector(to_s) のように指定しますが、RubyMotion では単に文字列を渡すだけで良いようになっています)

String/Array/Hash/Numeric/Time

Ruby の String, Array, Hash, Numeric, Time クラスと同様のクラスが Objective-C でも存在しています。RubyMotion では Objective-C のクラスを継承する形でそれらのクラスが作られています。

クラスクラスの継承関係
StringString → NSMutableString → NSString → NSObject
ArrayArray → NSMutableArray → NSArray → NSObject
HashHash → NSMutableDictionary → NSDictionary → NSObject
NumericNumeric → NSNumber → NSValue → NSObject
TimeTime → NSDate → NSObject

この継承関係のおかげで、Ruby の Time として作ったオブジェクトでも、NSDate のメソッドを使うことができたりします。

Objective-C で書かれたプログラムに Time オブジェクトを渡しても NSDate として振る舞ってくれるので Objective-C にとっても都合が良いということですね。私たちにとってはオブジェクトを変換する手間が不要で嬉しい!!

1
2
3
4
5
6
# Ruby メソッドでオブジェクトを作成
(main)> time = Time.now

# Objective-C メソッドを使う
(main)> time.timeIntervalSince1970
=> 1355993088.0

親のクラスである NSString, NSArray, NSDictionary, NSNumber, NSDate には互換性のために Ruby メソッドが追加されています。

1
2
3
4
5
6
7
8
# Objective-C メソッドでオブジェクトを作成
(main)> dateFormatter = NSDateFormatter.alloc.init
(main)> dateFormatter.setDateFormat("yyyy-MM-dd'T'HH:mm:ss+09:00")
(main)> date = dateFormatter.dateFromString("2012-12-15T11:43:09+09:00")

# Ruby メソッドを使う
(main)> date.strftime("%Y")
=> "2012"

というように、 どのメソッドで作られたか、どのメソッドを呼び出すのか意識しなくても Ruby が書けるようになっています。

オープンクラスでメソッドを追加する場合は上位クラスに

メソッドを使うぶんには、Ruby なのか Objective-C なのか意識しなくても良いようになっていますが、オープンクラスでメソッドを追加するときだけは少し注意してください。

オープンクラスで String に何かメソッドを追加した場合ですが、そのメソッドは継承元の NSString のメソッドで作られたオブジェクトからは利用することができません。

1
2
3
4
5
6
7
8
9
10
11
12
(main)> class String
(main)>   def foo
(main)>   end
(main)> end

(main)> str = NSString.stringWithString("abc")

(main)> str.foo
2012-12-20 18:10:04.523 test[3295:c07] undefined method `foo' for "abc":String (NoMethodError)
test(3295,0xac2ada28) malloc: *** error for object 0xa896f30: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
=> #<NoMethodError: undefined method `foo' for "abc":String>

オープンクラスでメソッドを追加する場合は上位の NSString や NSArray に追加するとハマることが少なくなるかと思います。

1
2
3
4
5
6
7
8
9
10
11
12
(main)> class NSString
(main)>   def hello
(main)>     "hello"
(main)>   end
(main)> end

(main)> str = NSString.stringWithString("abc")
(main)> str.hello
=> "hello"

(main)> "abc".hello
=> "hello"

Comments