web-dev-qa-db-fra.com

Ruby: Destructeurs?

Je dois occasionnellement créer des images avec rmagick dans un répertoire de cache.

Pour ensuite les supprimer rapidement, sans les perdre pour la vue, je souhaite supprimer les fichiers image pendant que mon instance Ruby de la classe Image est détruite ou entre dans le Garbage Collection.

Quel ClassMethod dois-je écraser pour alimenter le destructeur avec du code?

22
Joern Akkermann

Vous pouvez utiliser ObjectSpace.define_finalizer lorsque vous créez le fichier image. Il sera appelé lorsque le ramasse-miettes viendra le récupérer. Faites juste attention à ne pas faire référence à l'objet lui-même dans votre proc, sinon il ne sera pas collecté par le garbage man. (Ne ramassera pas quelque chose qui est en vie et coups de pied)

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

La solution de @ edgerunner a presque fonctionné. Fondamentalement, vous ne pouvez pas créer de clôture à la place de l'appel define_finalizer, car il capture la liaison du nom actuel selfname__. En Ruby 1.8, il semble que vous ne pouvez pas utiliser d'objet procconverti (avec to_proc) à partir d'une méthode liée à selfname__. Pour que cela fonctionne, vous avez besoin d'un objet procqui ne capture pas l'objet pour lequel vous définissez le finaliseur.

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

Les bizarreries du GC sont intéressantes à lire, mais pourquoi ne pas désallouer correctement les ressources en fonction de la syntaxe de langage existante?

Laissez-moi clarifier cela.

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

L'image est détruite à la fin de l'exécution du bloc. Commencez simplement un bloc, effectuez tous les traitements d'image à l'intérieur, puis laissez l'image se détruire elle-même. Ceci est analogue à l'exemple C++ suivant:

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
10
nurettin

Ruby a ObjectSpace.define_finalizer pour définir les finaliseurs sur les objets, mais son utilisation n'est pas encouragée ni limitée (par exemple, le finaliseur ne peut pas faire référence à l'objet pour lequel il est défini, sinon le finaliseur rendra l'objet inéligible pour le garbage collection).

3
Chuck

Il n'y a vraiment pas de destructeur en Ruby.

Ce que vous pouvez faire, c'est simplement effacer tous les fichiers qui ne sont plus ouverts ou utiliser la classe TempFile qui le fait pour vous.

Mettre à jour :

J'ai déjà affirmé que PHP, Perl et Python n'avaient pas de destructeurs, mais cela semble être faux, comme le fait remarquer igorw. Je ne les ai pas vus très souvent, cependant. Un destructeur correctement construit est essentiel dans tout langage basé sur les allocations, mais dans un langage récupéré, il devient facultatif.

2
tadman

Il existe une solution très simple à votre problème. Ruby Design vous encourage à faire toutes les actions de manière claire et précise. Pas besoin d'actions magiques dans le constructeur/destructeur. Oui, les constructeurs sont nécessaires pour attribuer l'état initial de l'objet, mais pas pour les actions "magiques". Permettez-moi d'illustrer cette approche sur une solution possible. Objectif: garder les objets d’image disponibles mais nettoyer les fichiers d’image en cache.

# 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