如何解冻Ruby中的对象?

在Ruby中,有Object#freeze ,它可以阻止对对象的进一步修改:

 class Kingdom attr_accessor :weather_conditions end arendelle = Kingdom.new arendelle.frozen? # => false arendelle.weather_conditions = 'in deep, deep, deep, deep snow' arendelle.freeze arendelle.frozen? # => true arendelle.weather_conditions = 'sun is shining' # !> RuntimeError: can't modify frozen Kingdom script = 'Do you want to build a snowman?'.freeze script[/snowman/] = 'castle of ice' # !> RuntimeError: can't modify frozen String 

但是,没有Object#unfreeze 。 有没有办法解冻冻结的王国?

是的,不是 。 没有任何直接使用标准API的方法。 但是,了解一下#freeze?是什么#freeze? 是的,你可以解决它。 注意:这里的所有内容都是MRI当前版本的实现细节,可能会有所变化。


CRuby中的对象存储在结构RVALUE
方便的是,结构中的第一件事是VALUE flags;
所有Object#freeze FL_FREEZE都设置了一个名为FL_FREEZE的标志,它实际上等于RUBY_FL_FREEZERUBY_FL_FREEZE基本上是标志中的第11位 。
解冻对象所需要做的就是取消第11位。

要做到这一点,你可以使用Fiddle ,它是标准库的一部分,让你修改C级语言:

 require 'fiddle' class Object def unfreeze Fiddle::Pointer.new(object_id * 2)[1] &= ~(1 << 3) end end 

Ruby中的非立即值对象存储在address = object_id * 2 。 请注意,区分这一点很重要,因此您会意识到这不会让您解冻Fixnum

由于我们想要改变第11位,我们必须使用第二个字节的第3位。 因此我们用[1]访问第二个字节。

~(1 << 3)移位1三个位置然后反转结果。 这样,掩码中唯一为零的位将是第三位,而所有其他位将是1

最后,我们只使用按位和&= )应用蒙版。


 foo = 'A frozen string'.freeze foo.frozen? # => true foo.unfreeze foo.frozen? # => false foo[/ (?=frozen)/] = 'n un' foo # => 'An unfrozen string' 

不,根据Object#freeze的文档 :

无法解冻冻结的物体。

冻结状态存储在对象内。 调用freeze设置冻结状态,从而防止进一步修改。 这包括对对象的冻结状态的修改。

关于您的示例,您可以改为分配一个新字符串:

 script = 'Do you want to build a snowman?' script.freeze script = script.dup if script.frozen? script[/snowman/] = 'castle of ice' script #=> "Do you want to build a castle of ice?" 

Ruby 2.3引入了String#+@ ,所以str.dup if str.frozen?你可以写+str而不是str.dup if str.frozen?

如上所述,将变量复制回自身也有效地解冻变量。

如上所述,这可以使用.dup方法完成:

var1 = var1.dup

这也可以通过以下方式实现:

var1 = Marshal.load(Marshal.dump(var1))

我一直在使用Marshal.load(Marshal.dump()

我没有使用.dup ,只是通过这篇文章了解到它。

我不知道Marshal.load(Marshal.dump(之间有什么区别Marshal.load(Marshal.dump()

如果他们做同样的事情或.dup更强大,那么风格上我更喜欢.dup.dup说明要做什么 – 复制这个东西,但它没有说明怎么做,而Marshal.load(Marshal.dump()不仅过于冗长,而且说明如何进行复制 -如果HOW部分与我无关,我不是指定HOW部分的粉丝。我想复制变量的值,我不在乎如何。