Rubyでは、ほとんどのオブジェクトは、クラス変数とインスタンス変数を格納するためにメモリを必要とします。このメモリが割り当てられると、Rubyはこのメモリ位置によって各オブジェクトを表します。オブジェクトが変数に割り当てられたり、関数に渡されたりする場合、渡されるのはこのメモリの場所であり、このメモリのデータではありません。シングルトンメソッドはこれを利用します。シングルトンメソッドを定義すると、Rubyはオブジェクトクラスを新しいシングルトンクラスにサイレントに置き換えます。各オブジェクトはそのクラスを格納するため、Rubyはオブジェクトのクラスを、シングルトンメソッドを実装する(そして元のクラスから継承する)新しいクラスに簡単に置き換えることができます。
これは、即時値であるオブジェクトtrue
(、、、、すべてのシンボル、およびFixnum内に収まるほど小さい整数)には当てはまりません。Rubyは、これらのオブジェクトのインスタンスにメモリを割り当てません。オブジェクトをメモリ内の場所として内部的に表しません。代わりに、内部表現に基づいてオブジェクトのインスタンスを推測します。これが意味することは2つあります。false
nil
各オブジェクトのクラスは、メモリの特定の場所に保存されなくなり、代わりに、直接のオブジェクトのタイプによって暗黙的に決定されます。これが、Fixnumsがシングルトンメソッドを持つことができない理由です。
同じ状態のイミディエイトオブジェクト(たとえば、整数2378の2つのFixnum)は、実際には同じインスタンスです。これは、インスタンスがこの状態によって決定されるためです。
これをよりよく理解するために、Fixnumでの次の操作を検討してください。
>> x = 3 + 7
=> 10
>> x.object_id == 10.object_id
=> true
>> x.object_id == (15-5).object_id
=> true
ここで、文字列を使用してそれらを検討します。
>> x = "a" + "b"
=> "ab"
>> x.object_id == "ab".object_id
=> false
>> x.object_id == "Xab"[1...3].object_id
=> false
>> x == "ab"
=> true
>> x == "Xab"[1...3]
=> true
FixnumのオブジェクトIDが等しい理由は、それらが同じ内部表現を持つ即時オブジェクトであるためです。一方、文字列は割り当てられたメモリに存在します。各文字列のオブジェクトIDは、メモリ内のオブジェクト状態の場所です。
いくつかの低レベルの情報
これを理解するには、Ruby(少なくとも1.8および1.9)がFixnumを内部でどのように処理するかを理解する必要があります。Rubyでは、すべてのオブジェクトはCコードで型の変数によって表されますVALUE
。Rubyは、次の要件を課していますVALUE
。
タイプVALUEは、ポインターを保持するのに十分なサイズの最小の整数です。これは、Cではそれを意味しsizeof(VALUE) == sizeof(void*)
ます。
非即時オブジェクトは、4バイト境界に揃える必要があります。これは、Rubyによって割り当てられたすべてのオブジェクトが4*i
整数のアドレスを持つことを意味しますi
。これは、すべてのポインタの最下位2ビットの値がゼロであることも意味します。
最初の要件により、Rubyはオブジェクトへのポインターと即値の両方を型の変数に格納できますVALUE
。2番目の要件により、Rubyは2つの最下位ビットに基づいてFixnumオブジェクトとSymbolオブジェクトを検出できます。
これをより具体的にするために、32ビットアーキテクチャでz
呼び出すRubyオブジェクトの内部バイナリ表現について考えてみます。Rz
MSB LSB
3 2 1
1098 7654 3210 9876 5432 1098 7654 32 10
XXXX XXXX XXXX XXXX XXXX XXXX XXXX AB CD
次に、RubyはRz
、の表現をz
次のように解釈します。
の場合D==1
、z
はFixnumです。このFixnumの整数値は、表現の上位31ビットに格納され、算術右シフトを実行してこれらのビットに格納されている符号付き整数を復元することによって復元されます。
3つの特別な表現がテストされます(すべてD==0
)
- の場合
Rz==0
、z
false
- の場合
Rz==2
、z
true
- の場合
Rz==4
、z
nil
の場合ABCD == 1110
、「z」はシンボルです。シンボルは、最下位8ビットを右シフトすることによって(つまり、z>>8
Cで)一意のIDに変換されます。32ビットアーキテクチャでは、これにより2 ^ 24の異なるID(1,000万を超える)が可能になります。64ビットアーキテクチャでは、これにより2^48の異なるIDが許可されます。
それ以外Rz
の場合は、Rubyオブジェクトのインスタンスのメモリ内のアドレスを表し、のタイプはz
その場所のクラス情報によって決定されます。