クラスの作成後にrubyが3つのオブジェクトを作成するのはなぜですか?


8

Rubyのメタクラスについて勉強していました。私はメタクラスが何であるかがうまく記述されているこの答え読みました。クラスが作成されると、2つのオブジェクトが作成されます。それは理解できます。1つはクラス自体用で、もう1つはメタクラス用です。しかし、自分で試してみると、3つのオブジェクトが作成されていることがわかります。

puts "Before Class Creation object count - #{ObjectSpace.count_objects[:T_CLASS]}"
class Test
  def self.foo # test_singleton
    p 'Printed from method #foo'
  end

  def bar # test
    p 'Printed from method #bar'
  end
end
puts "After Class Creation object count - #{ObjectSpace.count_objects[:T_CLASS]}"

###############

Before Class Creation object count - 949
After Class Creation object count - 952

使用していRuby - 2.5.1ます。

誰かがこれを理解するのを手伝ってくれる?

更新:

参照はSO Iの方法として、ルビー-1.9.1以上を使用して追加した後count_objectsためObjectSpace1.9.1に導入されました。と思われるT_CLASSカウントは常に常に3(と試みられていますruby-1.9.3-p551)。

だから、今までのところ、この答えがなぜなのかはまだ謎のままです。顕微鏡下でのルビーも数が2であると言います。


1
コマンドラインで実行すると、違いは2です。IRBで実行した場合の違いは3です。IRBは独自に何かをしているようです。IRBを使用して結果を得ましたか?いずれにせよ、ObjectSpaceIRB(およびおそらくPry)内で実行されるメソッドを実行すると、結果が歪んでしまいます。
Cary Swoveland

1
@CarySwoveland:私もその方向に考えていましたが、コマンドラインで実行した場合除いて、3も得られます。唯一の違いは、私が得る合計です。IRbの場合、1001と998を取得し(実行間でかなり一貫しています)、コマンドラインでは大幅に少なくなり、を使用する--disable-jit --disable-gems --disable-did_you_meanとさらに少なくなりますが、カウントは実行全体で常に一貫しており、常に3. macOS "Catalina" 10.15.4でHomebrewのYARV 2.7.1を使用しています。
イェルクWミッターク

@JörgWMittagand ...
Cary Swoveland

2
前回チェックしたとき、YARVは常にモジュールとクラスのシングルトンクラスをパフォーマンス最適化として熱心に作成していました。ただし、モジュールとクラスにはほとんど常にモジュール関数とクラスメソッドがあることを前提としています。クリアリー、@ CarySwovelandの調査結果によると、それはもはや真実ではありません。YARV内部の知識を更新する必要があります。(私はここ2、3年はTruffleRubyとRubiniusにずっと興味があり、最後の3年間は主にECMAScriptを行っていました。)それでも、その3番目のクラスの出所は謎です。
イェルクWミッターク

1
...ステファン達、私は間違って話しました。実行class Test; endすると、カウントの違いは2です。私が実行したときclass Test; def self.t; end; endの違いをクラスメソッドを作成すると、作成一見ので、3でTestのシングルトンクラスを。ただし、ObjectClass.each_object(Class)配列の違いが前後に実行される[Test]場合は、最初のケースと[Test, #<Class:Test>]2番目のケースです。
Cary Swoveland

回答:


6

https://bugs.ruby-lang.org/issues/16788から:

クラスを作成すると、シングルトンクラスが自動的に作成されます(ユーザーはアクセスできません)。クラスのシングルトンクラスを参照すると、そのシングルトンクラスのシングルトンクラスが自動的に作成されます。これは、メタクラスの継承構造の一貫性を保つためです。そうでない場合、クラスメソッドはスーパークラスのメタクラスから継承されません。これは、スーパークラスのクラスメソッドがサブクラスのクラスメソッドとして利用できる必要があるためです。

質問コードを少し変更する:

$old_classes = []
def print_objects
  new_classes = []
  ObjectSpace.each_object(Class){|x| new_classes << x}
  puts "New classes: #{new_classes - $old_classes}" unless $old_classes.empty?
  puts "Counts: #{ ObjectSpace.count_objects[:T_CLASS] }"
  $old_classes = new_classes
end

print_objects

class Test
end
puts 'Test class created'
print_objects

class Test
  def self.foo
  end 
end
puts 'Test singleton class referenced'
print_objects

次の結果が得られます。

Counts: 690
Test class created
New classes: [Test]
Counts: 692
Test singleton class referenced
New classes: [#<Class:Test>]
Counts: 693

コンソールの内側と外側の両方でRuby 2.6と2.0(数値は異なりますが、違いは同じです)と@SajibHassanを1.9.3(メソッドcount_objectsが導入されたバージョン)で試しました。これは、違いが常に3であり、作成された最初のシングルトンクラスにユーザーがアクセスできないことを意味します。

書籍「Ruby Under a Microscope」(2012年にRuby 2.1のリリース後に作成)は、2つのメタクラスのみの作成についても説明していますが、これは、得られる結果と一致しません。

Module#prependこの追加のクラスの考えられる理由としてコメントで@JörgWMittagによって言及された(Ruby 2.0で導入された)のようなメソッド は、を使用することに注意してくださいT_ICLASS。詳細については、メソッドが導入さたコミットを確認しください。T_ICLASS内部クラスを表すので、内部クラスはユーザーには表示されません(これは理にかなっています)。なぜT_CLASSユーザーがアクセスできるのか、アクセスできないのかはわかりません。


2
ruby v1.9.3p551でこれを実行しました。ただし、結果は同じカウント3です。1.9.1で導入されたメソッド「count_objects」のため、作成者は1.9.1以上を使用する必要があります。
Sajib Hassan

これはバグの可能性があると思い、報告しました:bugs.ruby-lang.org/issues/16788
AnaMaríaMartínezGómez

1
他のオブジェクトはシングルトンクラスのシングルトンクラス用であると予想されるというバグレポートへの返信があります。参考にして回答を編集してください。賞金を授与する時間はあまりありません。
Rafayet Monon

更新しました!なぜ2つのシングルトンクラスが必要なのかはまだ知りません。
AnaMaríaMartínezGómez

1
ええ、私もそれについて知りたいです。しかし、それはまた別の時期だと思います。回答していただき、ありがとうございました。
Rafayet Monon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.