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?
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
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 self
name__. En Ruby 1.8, il semble que vous ne pouvez pas utiliser d'objet proc
converti (avec to_proc
) à partir d'une méthode liée à self
name__. Pour que cela fonctionne, vous avez besoin d'un objet proc
qui 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
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
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).
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.
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