Ruby: Destructors?

前端 未结 6 1962
伪装坚强ぢ
伪装坚强ぢ 2020-11-30 04:26

I need to occasionaly create images with rmagick in a cache dir.

To then get rid of them fast, without loosing them for the view, I want to delete the image-files wh

相关标签:
6条回答
  • 2020-11-30 04:32

    You can use ObjectSpace.define_finalizer when you create the image file, and it will get invoked when the garbage man comes to collect. Just be careful not to reference the object itself in your proc, otherwise it won't be collected by the garbage man. (Won't pick up something that's alive and kicking)

    class MyObject
      def generate_image
        image = ImageMagick.do_some_magick
        ObjectSpace.define_finalizer(self, proc { image.self_destruct! })
      end
    end
    
    0 讨论(0)
  • 2020-11-30 04:46

    @edgerunner's solution almost worked. Basically, you cannot create a closure in place of the define_finalizer call since that captures the binding of the current self. In Ruby 1.8, it seems that you cannot use any proc object converted (using to_proc) from a method that is bound to self either. To make it work, you need a proc object that doesn't capture the object you are defining the finalizer for.

    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
    
    0 讨论(0)
  • 2020-11-30 04:46

    GC quirks are nice to read about, but why not properly deallocate resources according to already existing language syntax?

    Let me clarify that.

    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
    

    Image is destroyed after the block finishes executing. Just start a block, do all the image processing inside, then let the image destroy itself. This is analogous to the following C++ example:

    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
    
    0 讨论(0)
  • 2020-11-30 04:48

    There is very simple solution for your problem. Ruby design encourage you to do all actions in definite and clear way. No need for magic actions in constructor/destructor. Yes, constructors are required as a convenient way to assign initial state of object but not for "magic" actions. Let me illustrate this approach on possible solution. Goal, to keep image objects available but clean cache files of images.

    # 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 讨论(0)
  • 2020-11-30 04:50

    Ruby has ObjectSpace.define_finalizer to set finalizers on objects, but its use isn't exactly encouraged and it's rather limited (e.g. the finalizer can't refer to the object it is set for or else the finalizer will render the object ineligible for garbage collection).

    0 讨论(0)
  • 2020-11-30 04:55

    There's really no such thing as a destructor in Ruby.

    What you could do is simply clear out any files that are no longer open, or use the TempFile class which does this for you.

    Update:

    I previously claimed that PHP, Perl and Python do not have destructors, but this does appear to be false as igorw points out. I have not seen them used very often, though. A properly constructed destructor is essential in any allocation-based language, but in a garbage collected one it ends up being optional.

    0 讨论(0)
提交回复
热议问题