在对象空间object_id中复制类

jer*_*ere 7 ruby gem ruby-on-rails rails-engines

我有一个奇怪的问题,我正在使用的rails引擎中的某些模型在对象空间中被复制.

(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
Run Code Online (Sandbox Code Playgroud)

发生这种情况时,我不能使用is_a?或等式检查以测试对象是Field类的实例.问题只发生在开发中,看起来它可能是由cache_classes关闭引起的.我认为前一个请求中的对象仍然在对象空间中,但我不确定如何删除它.

Mar*_*une 2

这很容易通过以下方式重现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"
Run Code Online (Sandbox Code Playgroud)

正如您所说,只有在发育过程中才会出现这种症状。当 Rails 重新加载类时,它只是调用remove_const已定义的类,以强制重新加载它们(使用autoload)。这是代码DynamicFieldsets::Field.before_remove_const如果定义了,Rails 实际上会调用它,如此处所解释的,多好:-)

这些应该被垃圾收集,您可以使用 触发 GC GC.start,但是如果您有旧类的实例(如first_x我的示例)或子类,则旧类不能被垃圾收集。

请注意,从新实例将属于新类的is_a?意义上来说,应该可以正常工作。在我的例子中:kind_of?is_a?

first_x.is_a? X  # => false
second_x.is_a? X # => true
Run Code Online (Sandbox Code Playgroud)

这是正确的行为,因为X它指的是新类,而不是旧类。