为什么Rails Active支持deep_dup不会复制嵌入到被克隆对象中的数组?

时间:2017-07-23 07:23:03

标签: ruby-on-rails ruby-on-rails-5

我有一个在其状态下嵌入数组的对象。我在对象实例上使用deep_dup,但返回的对象引用与原始实例相同的数组对象,这不是我期望的。

以下是我的工作:

class A
  def initialize(arr)
    @arr = arr.deep_dup
  end

  attr_accessor :arr
end

a = A.new(['a', 'b', 'c'])
# => #<A:0x007fe6eb7ae458 @arr=["a", "b", "c"]> 
b = a.deep_dup
# => #<A:0x007fe6f382ad20 @arr=["a", "b", "c"]> 
b.arr[1] = 'e'
# => "e" 
a
# => #<A:0x007fe6eb7ae458 @arr=["a", "e", "c"]> 
b
# => #<A:0x007fe6f382ad20 @arr=["a", "e", "c"]> 
a.arr.object_id
# => 70314884952600 
b.arr.object_id
# => 70314884952600 

我已阅读deep_dup的文档,

  

deep_dup方法返回给定对象的深层副本。

我在做什么或理解错误?

1 个答案:

答案 0 :(得分:1)

如果您在Object

上查看deep_dup的来源
def deep_dup
  duplicable? ? dup : self
end

它只是调用dup(默认情况下duplicable?始终为true)。其中,对于数组,它在每个元素上调用deep_dup

def deep_dup
  map(&:deep_dup)
end

Object查看dup(因为那是A上最终被调用的内容):

  

生成obj的浅表副本 - 复制obj的实例变量,但不复制它们引用的对象。 dup复制obj的污点状态。

所以,为了让你的榜样有效,你需要这样的东西:

class A
  attr_accessor :array
  def initialize(array)
    @array = array.deep_dup
  end

  def initialize_copy(copy)
    copy.array = self.array.deep_dup

    super
  end
end

a = A.new(['a', 'b', 'c'])
b = a.deep_dup
b.array[1] = 'e'

puts a.inspect          # => #<A:0x007f857be2f510 @array=["a", "b", "c"]>
puts b.inspect          # => #<A:0x007f857be2f448 @array=["a", "e", "c"]>
puts a.array.object_id  # => 70105642924 560 (spaces added for clarity)
puts b.array.object_id  # => 70105642924 660

initialize_copy方法在任何地方都没有详细记录,但dup方法声明

  

此方法可能具有特定于类的行为。如果是这样,该行为将记录在类的#initialize_copy方法下。

因此,将其设置为自定义dup流程的方式,而不会实际覆盖dupthis文档(这是我可以找到的唯一真实文档方法)显示它只采用1参数,即副本