2016-02-25 17 views
23

は、Object#freezeオブジェクトにさらなる修正を防止する、がある:Rubyでオブジェクトを解凍する方法は? Rubyで

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がありません。凍った王国を解凍する方法はありますか?

答えて

43

はい、いいえです。標準APIを使用する直接的な方法はありません。ただし、#freeze?の機能を理解していれば、回避することができます。注:ここに記載されているのは、MRIの現在のバージョンの実装の詳細であり、変更される可能性があります。


CRubyのオブジェクトは、構造体RVALUEに格納されます。
便利なことに、構造体の最初のものはVALUE flags;です。
Object#freezeにはFL_FREEZEというフラグが設定されています。実際にはequal to RUBY_FL_FREEZEです。 RUBY_FL_FREEZEは基本的にフラグに11th bitになります。
オブジェクトをフリーズ解除するために必要な作業は、11番目のビットの設定を解除することです。

それを行うには、標準ライブラリの一部であり、あなたがCレベルの言語でいじくりできた、Fiddleを使用することができます=

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番目のビットを変更したいので、2番目のバイトの3番目のビットで作業する必要があります。したがって、[1]で2番目のバイトにアクセスします。

~(1 << 3) shiftts 1 3つの位置を指定し、結果を反転します。この方法では、マスク内のゼロである唯一のビットが3番目のビットになり、他のビットはすべてのものになります。

最後に、ビット単位で&=)のマスクを適用します。 documentation for Object#freezeに応じ


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

Rubyの内部でうんざりするのは危険です。あなたはあなたの答えにそれを述べるべきです。 – Stefan

+4

@Stefan、私は最初の段落でそれを言いました。私はそれが自明であると考えます。 – ndn

+0

私はそれが「危険」であるとは考えていません。それは完璧にうまくいっているので、私はndnと同意します。彼は非常に詳細な説明も提供していたので、多くのアップフォートがあるという驚きはありませんでした。 – shevy

12

いいえ、:

、冷凍物の凍結を解除する方法はありません。

フリーズ状態はオブジェクト内に格納されます。 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?" 

ルビー2:

あなたの例については、あなたの代わりに新しい文字列を割り当てることができます。3がString#[email protected]を導入したので、str.dup if str.frozen?の代わりに+strと書くことができます。

+1

私は特にあなたの追加がString#+ @部分に言及しているのが好きです。これはかなり新しくなっているので、それを言及しておくとよいでしょう。当時私は気が変わっていたことに気付きました。以前はチェンジログを使って変更を加えていましたが、+ strについてはわかりませんでした。 – shevy

-1

上記のように、変数を自身にコピーすると、変数が効果的にフリーズ解除されます。 ... )私はMarshal.load(Marshal.dump(を使用している

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

:これはまた、使用して達成することができる

var1 = var1.dup

たように、これは.dupメソッドを使用して行うことができ指摘しました

私は.dupを使用していません。この投稿を通してそれについて学んだ。

私は、彼らが同じことを行うか.dupがより強力であればどんな違いがMarshal.load(Marshal.dump( ... )

間がある場合、その文体、私はより良い.dupが好き。知りません.dupは何をすべきかを述べていますが、コピーする方法は言いませんが、Marshal.load(Marshal.dump( ... )は過度に冗長ではありませんが、複製の仕方を述べています。 HOW部分の場合 HOW部分は私には無関係です。私は変数の値を複製したい、私はどのように気にしない。

関連する問題