如何解冻Ruby中的对象?

Ruby中有 Object#freeze,可以防止对对象的进一步修改
class Kingdom
  attr_accessor :weather_conditions
end

arendelle = Kingdom.new
arendelle.frozen? # => false
arendelle.weather_conditions = 'in 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

但是,没有对象#解冻.有没有办法解冻冻结的王国?

解决方法

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

CRuby中的对象存储在struct RVALUE中.
方便的是,结构中的第一件事是VALUE标志.
所有对象#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存储在address =他们的object_id * 2上.请注意,重要的是进行区分,以便您知道这不会让您解冻Fixnums.

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

〜(1 <3)移动3个位置,然后使结果反转.这样,掩码中零位的唯一位将是第三个,所有其他将是一个. 最后,我们只用位和(& =)应用掩码.

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

相关文章

以下代码导致我的问题: class Foo def initialize(n=0) @n = n end attr_accessor :n d...
这是我的spec文件,当为上下文添加测试“而不是可单独更新用户余额”时,我得到以下错误. require 's...
我有一个拦截器:DevelopmentMailInterceptor和一个启动拦截器的inititializer setup_mail.rb. 但我想将...
例如,如果我有YAML文件 en: questions: new: 'New Question' other: recent: ...
我听说在RSpec中避免它,let,let !,指定,之前和主题是最佳做法. 关于让,让!之前,如果不使用这些,我该如...
我在Rails中使用MongoDB和mongo_mapper gem,项目足够大.有什么办法可以将数据从Mongoid迁移到 Postgres...