如何解冻Ruby中的对象?

时间:2016-02-25 16:48:23

标签: 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。有没有办法解冻冻结的王国?

4 个答案:

答案 0 :(得分:54)

是和否。没有任何直接使用标准API的方法。但是,通过对#freeze?的作用有所了解,您可以解决它。注意:这里的所有内容都是MRI当前版本的实施细节,可能会有所变化。

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

为此,您可以使用Fiddle,它是标准库的一部分,可让您修改C级语言:

require 'fiddle'

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

immediate value objects in Ruby存储在地址=他们的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'

答案 1 :(得分:21)

不,根据documentation for 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而不是str.dup if str.frozen?

答案 2 :(得分:0)

frozen_object = %w[hello world].freeze
frozen_object.concat(['and universe']) # FrozenError (can't modify frozen Array)
frozen_object.dup.concat(['and universe']) # ['hello', 'world', 'and universe']

答案 3 :(得分:-1)

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

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

var1 = var1.dup

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

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

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

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

我不知道Marshal.load(Marshal.dump( ... )

之间有什么区别

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