大家觉得标题的写法如何? 我这样写标题只有一个目的,在表达字面意思的同时体现出他们之间的组合情况。
准备在开讲前 什么是浅拷贝?什么是深拷贝?
拷贝的目的是为了得到两个看起来一样但是本质上又不同的对象,这里的本质体现在他们是否指向了同一个存储空间。
在 Ruby 中,对象的定义是一组实例变量外加一个指向其类的引用。如果其某个实例变量又依赖于另外的对象,那么这个时候我们如何来对待这个依赖的对象呢?
根据我们处理方式的不同将会得到两种不同的拷贝概念 浅拷贝:对所有的依赖对象不做拷贝,仅仅是引用 深拷贝:对所有的依赖(依赖的依赖)对象都做拷贝
第一种组合情况:Ruby - 浅拷贝
在 Ruby 中有两个方法来实现浅拷贝clone, dup
,他们都是Object
mix in Kernel
得到的方法。
他们之间有个微小的差别,dup
将不会对 extended 的 modules 进行拷贝。
第二种组合情况:Ruby - 深拷贝
Ruby 并没有一个现成的方法来实现对象的深拷贝,目前一个常用的方法是通过Marshal
来实现
Marshal.load(Marshal.dump(obj))
第三种组合情况:Rails - 浅拷贝 Rails 并没有对 Ruby 的浅拷贝做什么的扩展
第四种组合情况:Rails - 深拷贝 说到这里顺便就吐槽一下 Rails 官方指导文档的描述真心不够贴切(大家可以对比第二个链接中的源代码): http://guides.rubyonrails.org/active_support_core_extensions.html#deep-duplicating
ActiveSupport 中 deep_dup 源码 https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/deep_dup.rb
从源码我们可以看到,Rails 对数组和 Hash 做了深拷贝的扩展,而对 Object 的扩展没有任何实际意义,仅仅是辅助于数组和 Hash 的扩展。也就是说,加载了 Rails 之后,我们可以很方便的对数组和 Hash 做深拷贝。但是对于普通对象而言,依然无能为力。 我们看看官方指导文档的描述:
2.4 deep_dup The deep_dup method returns deep copy of a given object. Normally, when you dup an object that contains other objects, Ruby **does not dup them, so it creates a shallow copy of the object.
看后是否觉得,Yes,good job! 但是,实际上它并没有真的做到。 那么针对这样的情况,我们不得不把 Ruby - 深拷贝 中提到的深拷贝方法搬过来继续用!
下面看一个普通对象的拷贝例子: 源码出自: http://alancohen.tumblr.com/post/35030966381/shallow-vs-deep-copy
class Obj
attr_accessor :first, :second
def initialize
@first = {:one => 'x',:two => 'y',:three => 'z'}
@second = {[1,2]=>'x',[3,2]=>'o'}
end
end
# 浅拷贝
obj1 = Obj.new
obj2 = obj1.clone
obj1.object_id != obj2.object_id
# 拷贝对象和源对象都指向同样的依赖
obj1.first.object_id == obj2.first.object_id
# 深拷贝
obj3 = Marshal.load( Marshal.dump(obj1) )
# 依赖也被拷贝了
obj3.first.object_id != obj1.first.object_id