【问题标题】:Duplicating class in the object space object_id在对象空间 object_id 中复制类
【发布时间】:2024-01-09 22:44:01
【问题描述】:

我遇到了一个奇怪的问题,我正在使用的 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

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

【问题讨论】:

  • +1,这看起来不可能。怎么会有两个对象绑定到同一个常量? puts "#{klass.inspect}: #{klass.object_id}" if ... 输出什么?
  • DynamicFieldsets::Field 是什么排序类?它来自哪里?
  • DynamicFieldsets::Field 是一个来自 rails 引擎的 ActiveRecord::Base 对象。当您检查对象空间中的类时,除了对象 ID 之外,它们完全相同。任何检查是否相等的方法都将返回 true,除了检查对象是否相等的方法,例如 is_a? .
  • +1 这很奇怪...@Matheus Moreira 说了什么

标签: ruby-on-rails ruby gem rails-engines


【解决方案1】:

这很容易用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"

正如您所说,您只会在开发过程中出现这种症状。当 Rails 重新加载类时,它只是在定义的类上调用remove_const,以强制重新加载它们(使用autoload)。 Here's the code。 Rails 实际上会调用DynamicFieldsets::Field.before_remove_const,如果它被定义了,正如here 解释的那样,多好:-)

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

请注意,is_a? 应该可以正常工作,因为新实例将是新类的 kind_of?is_a?。在我的例子中:

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

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

【讨论】:

    最近更新 更多