在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
。有没有办法解冻冻结的王国?
答案 0 :(得分:54)
是和否。没有任何直接使用标准API的方法。但是,通过对#freeze?
的作用有所了解,您可以解决它。注意:这里的所有内容都是MRI当前版本的实施细节,可能会有所变化。
CRuby中的对象存储在结构RVALUE
中
方便的是,结构中的第一件事是VALUE flags;
所有Object#freeze
都设置了一个名为FL_FREEZE
的标记,实际上是equal to RUBY_FL_FREEZE
。 RUBY_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 部分的粉丝。我想复制变量的值,我不在乎如何。