2012-03-06 26 views
7

Tôi gặp vấn đề lạ khi một số mô hình nhất định trong công cụ đường ray mà tôi đang sử dụng đang được nhân bản trong không gian đối tượng.Lớp nhân bản trong không gian đối tượng object_id

(rdb:1) ObjectSpace.each_object(::Class).each { |klass| puts klass.to_s + ": " + klass.object_id.to_s if klass.to_s.eql?("DynamicFieldsets::Field") } 
DynamicFieldsets::Field: 66866100 
DynamicFieldsets::Field: 71836380 
2479 

Khi điều này xảy ra, tôi không thể sử dụng is_a? hoặc kiểm tra bình đẳng để kiểm tra rằng một đối tượng là một thể hiện của lớp Field. Vấn đề chỉ xảy ra trong phát triển và có vẻ như nó có thể được gây ra bởi cache_classes bị tắt. Tôi nghĩ rằng các đối tượng từ yêu cầu trước đó vẫn còn trong không gian đối tượng nhưng tôi không chắc chắn làm thế nào để loại bỏ nó.

+1

+1, điều này có vẻ không thể. Làm thế nào có thể có hai đối tượng liên kết với cùng một hằng số? 'Puts" # {klass.inspect}: # {klass.object_id} "nếu ...' đầu ra là gì? –

+0

Lớp sắp xếp nào là 'DynamicFieldsets :: Field'? Nó đến từ đâu? –

+0

DynamicFieldsets :: Field là một đối tượng ActiveRecord :: Base đến từ một engine ray. Khi bạn kiểm tra các lớp trong không gian đối tượng, chúng giống hệt nhau trừ đối tượng id. Bất kỳ phương pháp nào để kiểm tra xem liệu các giá trị này có bằng nhau hay không sẽ trả lại giá trị true, ngoại trừ các phương thức kiểm tra đối tượng, chẳng hạn như is_a? . – jeremiahishere

Trả lời

2

này rất dễ dàng để tái tạo với remove_const:

class X 
    def self.foo 
    "hello" 
    end 
end 
first_x = X.new 

Object.send :remove_const, :X 
class X 
    def self.foo 
    "world" 
    end 
end 
second_x = X.new 

p first_x.class, first_x.class.object_id, second_x.class, second_x.class.object_id 
    # => X, <an_id>, X, <another_id> 
p first_x.class.foo, second_x.class.foo 
    # => "hello", "world" 

Như bạn nói, bạn sẽ có triệu chứng này chỉ trong phát triển. Khi Rails tải lại các lớp, nó chỉ cần gọi remove_const trên các lớp đã định nghĩa, buộc chúng phải được nạp lại (sử dụng autoload). Here's the code. Rails sẽ thực sự gọi DynamicFieldsets::Field.before_remove_const nếu nó được xác định, như được giải thích here, cách tốt đẹp :-)

Đây phải là rác được thu thập và bạn có thể kích hoạt GC với GC.start, nhưng nếu bạn có trường hợp của các lớp học cũ nằm xung quanh (như first_x trong ví dụ của tôi), hoặc các lớp con, các lớp cũ không thể được thu gom rác.

Lưu ý rằng is_a? sẽ hoạt động tốt, theo nghĩa là các phiên bản mới sẽ là kind_of?is_a? của lớp mới. Trong ví dụ của tôi:

first_x.is_a? X # => false 
second_x.is_a? X # => true 

Đây là hành vi đúng, vì X là lớp mới, không phải lớp cũ.

Các vấn đề liên quan