Warum erstellt Ruby 3 Objekte, nachdem eine Klasse erstellt wurde?

8

Ich habe über Rubys Metaklasse studiert. Ich habe diese Antwort gelesen, in der gut beschrieben ist, was Metaklasse ist. Es wird dort angezeigt, wenn eine Klasse erstellt wird, werden zwei Objekte erstellt. Welches ist verständlich. Eine für die Klasse selbst und eine für die Metaklasse. Aber wenn ich es selbst versuche, sehe ich, dass es drei Objekte schafft.

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

Ich benutze Ruby - 2.5.1.

Kann mir jemand helfen, diesen zu verstehen?

Aktualisieren:

Der von mir hinzugefügte SO-Referenzbeitrag verwendet Ruby-1.9.1 oder höher, da die Methode count_objectsfür ObjectSpacein 1.9.1 eingeführt wurde. Es scheint, dass die T_CLASSZählung immer 3 war (versucht mit ruby-1.9.3-p551).

Bis jetzt ist es immer noch ein Rätsel, warum diese Antwort . Ruby unter dem Mikroskop sagt auch, dass die Anzahl 2 ist.

Rafayet Monon
quelle
1
Bei Ausführung über die Befehlszeile beträgt der Unterschied 2. Bei Ausführung mit IRB beträgt der Unterschied 3. IRB scheint etwas alleine zu machen. Haben Sie Ihr Ergebnis mit IRB erhalten? In jedem Fall führen die Ausführung von ObjectSpaceMethoden, die innerhalb von IRB (und möglicherweise Pry) ausgeführt werden, zu verzerrten Ergebnissen.
Cary Swoveland
1
@CarySwoveland: Ich habe auch in diese Richtung gedacht, außer wenn ich es über die Kommandozeile ausführe, bekomme ich auch 3. Der einzige Unterschied ist die Summe, die ich bekomme. Wenn ich in IRb 1001 und 998 erhalte (und es ist ziemlich konsistent zwischen den Läufen), bekomme ich in der Befehlszeile deutlich weniger, und wenn ich es verwende --disable-jit --disable-gems --disable-did_you_mean, bekomme ich noch weniger, aber die Anzahl ist über die Läufe hinweg immer konsistent und unterscheidet sich immer um 3. Ich verwende YARV 2.7.1 von Homebrew unter macOS "Catalina" 10.15.4.
Jörg W Mittag
@ JörgWMittag und ...
Cary Swoveland
2
Als ich das letzte Mal nachgesehen habe, hat YARV eifrig immer Singleton-Klassen für Module und Klassen als Leistungsoptimierung erstellt, unter der Annahme, dass Module und Klassen fast immer Modulfunktionen und Klassenmethoden haben. Laut den Erkenntnissen von @ CarySwoveland ist dies nicht mehr der Fall. Ich muss mein Wissen über YARV-Interna wirklich aktualisieren. (Ich habe mich in den letzten Jahren viel mehr für TruffleRuby und Rubinius interessiert und in den letzten drei Jahren hauptsächlich für ECMAScript.) Es ist immer noch ein Rätsel, woher diese dritte Klasse kommt.
Jörg W Mittag
1
... Stefan und andere, ich habe falsch geschrieben. Wenn ich laufe, class Test; endbeträgt der Unterschied in der Anzahl 2; Wenn ich class Test; def self.t; end; endden Unterschied ausführe, beträgt er 3, anscheinend weil beim Erstellen der Klassenmethode die TestSingleton-Klasse erstellt wird. Wenn ich jedoch ObjectClass.each_object(Class)vor und nach dem Unterschied in den Arrays laufe, ist dies [Test]im ersten und [Test, #<Class:Test>]im zweiten Fall der Fall .
Cary Swoveland

Antworten:

6

Von https://bugs.ruby-lang.org/issues/16788 :

Durch das Erstellen einer Klasse wird automatisch eine Singleton-Klasse erstellt (auf die der Benutzer nicht zugreifen kann). Durch Verweisen auf die Singleton-Klasse einer Klasse wird automatisch eine Singleton-Klasse dieser Singleton-Klasse erstellt. Dies dient dazu, die Konsistenz der Vererbungsstruktur von Metaklassen zu gewährleisten. Andernfalls würden Klassenmethoden nicht von der Metaklasse der Oberklasse erben, was erforderlich ist, da die Klassenmethoden der Oberklasse als Klassenmethoden der Unterklasse verfügbar sein sollten.

Ändern Sie den Fragencode ein wenig:

$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

Ich erhalte folgende Ergebnisse:

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

Ich habe es mit Ruby 2.6 und 2.0 sowohl innerhalb als auch außerhalb einer Konsole versucht (die Zahlen unterscheiden sich, aber der Unterschied ist der gleiche) und @SajibHassan mit 1.9.3 (Version, in der die Methode count_objectseingeführt wurde). Dies bedeutet, dass der Unterschied immer 3 war und dass die erste erstellte Singleton-Klasse für den Benutzer nicht zugänglich ist.

Das Buch Ruby Under a Microscope (geschrieben 2012 nach der Veröffentlichung von Ruby 2.1) beschreibt auch die Erstellung von nur zwei Metaklassen, die nicht mit dem Ergebnis übereinstimmen, das wir erhalten.

Beachten Sie, dass Methoden wie Module#prepend(eingeführt in Ruby 2.0), die von @ JörgWMittag in den Kommentaren als möglicher Grund für diese zusätzliche Klasse erwähnt wurden, verwendet werden T_ICLASS. Überprüfen Sie das Commit, in dem die Methode eingeführt wurde, auf Details. Ich denke, das T_ICLASSsteht für interne Klasse und folglich sollten interne Klassen für den Benutzer nicht sichtbar sein (was Sinn macht). Ich bin mir jedoch nicht sicher, warum einige T_CLASSfür den Benutzer zugänglich sind und andere nicht.

Ana María Martínez Gómez
quelle
2
Ich habe dies in Ruby v1.9.3p551 ausgeführt. Das Ergebnis ist jedoch die gleiche Anzahl. 3. Der Autor muss aufgrund der in 1.9.1 eingeführten Methode "count_objects"> = 1.9.1 verwenden.
Sajib Hassan
Ich denke, dies könnte ein Fehler sein, den ich gemeldet habe: bugs.ruby-lang.org/issues/16788
Ana María Martínez Gómez
1
Ihr Fehlerbericht enthält eine Antwort, die besagt, dass dies erwartet wird, da das andere Objekt für die Singleton-Klasse der Singleton-Klasse bestimmt ist. Können Sie bitte Ihre Antwort mit der Referenz bearbeiten. Ich habe nicht mehr viel Zeit, um das Kopfgeld zu vergeben.
Rafayet Monon
Aktualisiert! Obwohl ich immer noch neugierig bin, warum zwei Singleton-Klassen benötigt werden.
Ana María Martínez Gómez
1
Ja, darauf bin ich auch neugierig. Aber es ist für ein anderes Mal, denke ich. Vielen Dank, dass Sie geantwortet haben und dem auf den Grund gegangen sind.
Rafayet Monon