web-dev-qa-db-ja.com

Ruby:デストラクタ?

キャッシュディレクトリにrmagickで画像を作成する必要がある場合があります。

次に、ビューでそれらを失うことなく、それらをすばやく削除するために、私はRuby Image-Classのインスタンスが破壊されるかガベージコレクションに入る間に画像ファイルを削除します。

デストラクタにコードを提供するには、どのClassMethodを上書きする必要がありますか?

24
Joern Akkermann

ObjectSpace.define_finalizer 画像ファイルを作成すると、ゴミの人が収集するときに呼び出されます。自分のprocでオブジェクト自体を参照しないように注意してください。そうしないと、ガベージマンによってオブジェクトが収集されません。 (生きていて蹴っているものを拾わないでください)

class MyObject
  def generate_image
    image = ImageMagick.do_some_magick
    ObjectSpace.define_finalizer(self, proc { image.self_destruct! })
  end
end
21
edgerunner

@edgerunnerのソリューションはほぼ機能しました。基本的に、define_finalizer呼び出しの代わりにクロージャーを作成することはできません。これは、現在のselfのバインディングをキャプチャするためです。 Ruby 1.8では、procにバインドされているメソッドから変換されたselfオブジェクトを(to_procを使用して)変換することはできないようです。これを機能させるには、ファイナライザを定義しているオブジェクトをキャプチャしないprocオブジェクトが必要です。

class A
  FINALIZER = lambda { |object_id| p "finalizing %d" % object_id }

  def initialize
    ObjectSpace.define_finalizer(self, self.class.method(:finalize))  # Works in both 1.9.3 and 1.8
    #ObjectSpace.define_finalizer(self, FINALIZER)                    # Works in both
    #ObjectSpace.define_finalizer(self, method(:finalize))            # Works in 1.9.3
  end

  def self.finalize(object_id)
    p "finalizing %d" % object_id
  end

  def finalize(object_id)
    p "finalizing %d" % object_id
  end
end

a = A.new
a = nil

GC.start
25
Su Zhang

GCの癖は読みやすいですが、既存の言語構文に従ってリソースを適切に割り当て解除しないのはなぜですか?

はっきりさせておきます。

class ImageDoer
  def do_thing(&block)
    image= ImageMagick.open_the_image # creates resource
    begin
      yield image # yield execution to block
    rescue
      # handle exception
    ensure
      image.destruct_sequence # definitely deallocates resource
    end
  end
end

doer= ImageDoer.new
doer.do_thing do |image|
  do_stuff_with_image # destruct sequence called if this throws
end # destruct_sequence called if execution reaches this point

ブロックの実行が完了すると、イメージは破棄されます。ブロックを開始し、内部ですべての画像処理を行ってから、画像自体を破壊します。これは、次のC++の例に似ています。

struct Image
{
  Image(){ /* open the image */ }
  void do_thing(){ /* do stuff with image */ }
  ~Image(){ /* destruct sequence */ }
};

int main()
{
  Image img;
  img.do_thing(); // if do_thing throws, img goes out of scope and ~Image() is called
} // special function ~Image() called automatically here
12
nurettin

RubyにはObjectSpace.define_finalizerファイナライザをオブジェクトに設定しますが、その使用は厳密には推奨されておらず、かなり制限されています(たとえば、ファイナライザは、設定されているオブジェクトを参照できないか、ファイナライザがオブジェクトをガベージコレクションに適さないようにします)。

3
Chuck

Rubyにはデストラクタのようなものは本当にありません。

あなたができることは、もはや開いていないファイルをすべてクリアするか、これを行うTempFileクラスを使用することです。

更新

私は以前、PHP、PerlおよびPythonにはデストラクタがないと主張しましたが、igorwが指摘するように、これは誤りのようです。ただし、それらが頻繁に使用されることはありません。適切に構築されたデストラクタ割り当てベースの言語では必須ですが、ガベージコレクションされたものでは、オプションになります。

2
tadman

あなたの問題に対する非常に簡単な解決策があります。 Ruby設計では、すべてのアクションを明確で明確な方法で実行することが推奨されます。コンストラクタ/デストラクタでマジックアクションを実行する必要はありません。オブジェクトの初期状態を割り当てる便利な方法としてコンストラクタが必要ですが、 「マジック」アクションの場合。このアプローチを可能な解決策で説明します。目的は、イメージオブジェクトを使用可能に保つが、イメージのキャッシュファイルをクリーンにすることです。

# you are welcome to keep an in memory copy of the image
# GC will take care of it.
class MyImage
  RawPNG data
end

# this is a worker that does operations on the file in cache directory.
# It knows presizely when the file can be removed (generate_image_final)
# no need to wait for destructor ;)
class MyImageGenerator
  MyImage @img

  def generate_image_step1
    @image_file = ImageLib.create_file
  end
  def generate_image_step2
    ImageLib.draw @image_file
  end
  def generate_image_final
    @img=ImageLib.load_image @image_file
    delete_that_file @image_file
  end

  def getImage
    # optional check image was generated
    return @img
  end
end
0
smile-on