在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
Run Code Online (Sandbox Code Playgroud)
但是,没有Object#unfreeze.有没有办法解冻冻结的王国?
ndn*_*kov 54
是的,不是.没有任何直接使用标准API的方法.但是,通过对某些操作有所了解#freeze?,您可以解决它.注意:这里的所有内容都是MRI当前版本的实现细节,可能会有所变化.
CRuby中的对象存储在结构中RVALUE.
方便的是,结构中的第一件事是VALUE flags;.
所有的Object#freeze都设置了一个标志,称为FL_FREEZE,实际上等于RUBY_FL_FREEZE.RUBY_FL_FREEZE将基本上是标志中的第11位.
解冻对象所需要做的就是取消第11位.
要做到这一点,你可以使用Fiddle,这是标准库的一部分,让你修改C级语言:
require 'fiddle'
class Object
def unfreeze
Fiddle::Pointer.new(object_id * 2)[1] &= ~(1 << 3)
end
end
Run Code Online (Sandbox Code Playgroud)
Ruby中的非立即值对象存储在address = theobject_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'
Run Code Online (Sandbox Code Playgroud)
Ste*_*fan 24
不,根据以下文件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?"
Run Code Online (Sandbox Code Playgroud)
Ruby 2.3的介绍String#+@,所以你可以写+str而不是str.dup if str.frozen?
小智 5
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']
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11501 次 |
| 最近记录: |