Tại sao ruby ​​tạo 3 đối tượng sau khi một lớp được tạo?


8

Tôi đang nghiên cứu về siêu dữ liệu của Ruby. Tôi đọc câu trả lời này trong đó nó được mô tả độc đáo metaclass là gì. Nó được hiển thị ở đó khi một lớp được tạo, nó sẽ tạo hai đối tượng. Đó là điều dễ hiểu. Một cho chính lớp và một cho siêu dữ liệu. Nhưng khi tôi đang thử nó, tôi thấy rằng nó đang tạo ra ba đối tượng.

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

Tôi đang sử dụng Ruby - 2.5.1.

Bất cứ ai có thể giúp tôi hiểu điều này?

Cập nhật:

Các tài liệu tham khảo SO đăng bài mà tôi đã thêm được sử dụng ruby-1.9.1 hoặc cao hơn, như là phương pháp count_objectscho ObjectSpaceđã được giới thiệu trong 1.9.1. Có vẻ như T_CLASSsố đếm luôn luôn là 3 (đã thử với ruby-1.9.3-p551).

Vì vậy, cho đến bây giờ nó vẫn là một bí ẩn tại sao câu trả lời này . Ruby dưới kính hiển vi cũng cho biết số lượng là 2.


1
Khi chạy ở dòng lệnh sự khác biệt là 2. Khi chạy với IRB, sự khác biệt là 3. IRB dường như đang tự làm một cái gì đó. Bạn đã nhận được kết quả của bạn bằng IRB? Trong mọi trường hợp, các ObjectSpacephương thức thực thi chạy trong IRB (và Pry, có lẽ) cho kết quả bị bóp méo.
Cary Swoveland

1
@CarySwoveland: Tôi cũng đã suy nghĩ theo hướng đó, ngoại trừ khi tôi chạy nó trên dòng lệnh, tôi cũng nhận được 3. Sự khác biệt duy nhất là tổng số tôi nhận được. Khi ở IRb, tôi nhận được 1001 và 998 (và nó khá nhất quán giữa các lần chạy), trên dòng lệnh, tôi nhận được ít hơn đáng kể và khi tôi sử dụng --disable-jit --disable-gems --disable-did_you_mean, tôi thậm chí còn ít hơn, nhưng số lượng luôn luôn nhất quán trong các lần chạy và luôn khác nhau bởi 3. Tôi đang sử dụng YARV 2.7.1 từ Homebrew trên macOS "Catalina" 10.15.4.
Jörg W Mittag

@ JörgWMittag và ...
Cary Swoveland

2
Lần trước tôi đã kiểm tra, YARV luôn háo hức luôn tạo các lớp singleton cho các mô-đun và các lớp để tối ưu hóa hiệu suất, theo giả định, các mô-đun và lớp hầu như sẽ luôn có các hàm mô-đun và phương thức lớp. Cleary, theo phát hiện của @ CarySwoveland, điều đó không còn đúng nữa. Tôi thực sự cần cập nhật kiến ​​thức về nội bộ YARV. (Tôi đã quan tâm nhiều hơn đến TruffleRuby và Rubinius trong vài năm qua và chủ yếu thực hiện ECMAScript trong ba năm qua.) Đây vẫn là một bí ẩn mà lớp thứ ba đến từ đâu.
Jörg W Mittag

1
... Stefan và những người khác, tôi nhớ sai. Khi tôi chạy class Test; endsự khác biệt về số lượng là 2; Khi tôi chạy class Test; def self.t; end; endsự khác biệt là 3, dường như bởi vì việc tạo phương thức lớp sẽ tạo ra Testlớp đơn lẻ. Tuy nhiên, nếu tôi chạy ObjectClass.each_object(Class)trước và sau sự khác biệt trong các mảng là [Test]trong trường hợp đầu tiên và [Test, #<Class:Test>]trong lần thứ hai.
Cary Swoveland

Câu trả lời:


6

Từ https://bugs.ruby-lang.org/issues/16788 :

Tạo một lớp sẽ tự động tạo một lớp singleton (người dùng không thể truy cập được). Tham chiếu lớp singleton của một lớp sẽ tự động tạo ra một lớp singleton của lớp singleton đó. Điều này là để giữ sự thống nhất của cấu trúc thừa kế của metaclass. Mặt khác, các phương thức lớp sẽ không kế thừa từ siêu dữ liệu của lớp cha, điều này là cần thiết vì các phương thức lớp của lớp cha nên có sẵn như là các phương thức lớp của lớp con.

Sửa đổi mã câu hỏi một chút:

$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

Tôi nhận được kết quả sau:

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

Tôi đã thử nó với Ruby 2.6 và 2.0 cả bên trong và bên ngoài bảng điều khiển (các số khác nhau nhưng sự khác biệt là như nhau) và @SajibHassan với 1.9.3 (phiên bản mà phương thức count_objectsđược giới thiệu). Điều này có nghĩa là sự khác biệt luôn là 3 và lớp singleton đầu tiên được tạo không thể truy cập được đối với người dùng.

Cuốn sách Ruby Under a Kính hiển vi (được viết vào năm 2012 sau khi phát hành Ruby 2.1) cũng mô tả việc tạo ra chỉ hai siêu dữ liệu, không phù hợp với kết quả mà chúng tôi nhận được.

Lưu ý rằng các phương thức như Module#prepend(được giới thiệu trong Ruby 2.0), được đề cập bởi @ JörgWMittag trong các nhận xét là lý do có thể cho lớp bổ sung này, sử dụng T_ICLASS. Kiểm tra cam kết trong đó phương thức được giới thiệu để biết chi tiết. Tôi đoán rằng đó T_ICLASSlà viết tắt của lớp bên trong và do đó các lớp bên trong không nên hiển thị cho người dùng (điều này có ý nghĩa). Tôi không chắc chắn mặc dù tại sao một số T_CLASSngười dùng có thể truy cập và một số khác thì không.


2
Tôi đã chạy nó trong ruby ​​v1.9.3p551. nhưng kết quả là cùng một số 3. Tác giả phải sử dụng> = 1.9.1 vì phương thức "Count_objects" được giới thiệu trong 1.9.1.
Sajib Hassan

Tôi nghĩ rằng đây có thể là một lỗi, tôi đã báo cáo: bug.ruby-lang.org/issues/16788
Ana María Martínez Gómez

1
Có một câu trả lời trên báo cáo lỗi của bạn cho biết nó được mong đợi vì đối tượng khác dành cho lớp singleton của lớp singleton. Bạn có thể vui lòng chỉnh sửa câu trả lời của bạn với tài liệu tham khảo. Tôi không còn nhiều thời gian để trao tiền thưởng.
Rafayet Monon

Đã cập nhật! Mặc dù tôi vẫn tò mò tại sao cần hai lớp đơn.
Ana María Martínez Gómez

1
Vâng tôi cũng tò mò về điều đó. Nhưng đó là một lần khác tôi đoán. Cảm ơn bạn đã trả lời và đi đến tận cùng của nó.
Rafayet Monon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.