Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #==============================================================================
- # Iavra Weak Reference 1.01
- # -----------------------------------------------------------------------------
- # Description:
- # Allows access to objects without holding a direct reference. If an object is
- # only referenced by weak references, it may be garbage collected at any time.
- # -----------------------------------------------------------------------------
- # Prerequisites:
- # None
- # -----------------------------------------------------------------------------
- # How to Use:
- # A weak reference to <object> is created and accessed like this:
- #
- # weakref = IAVRA::WeakRef.new(<object>)
- # weakref.object
- #
- # The object method will return nil if <object> is no longer accessible. Objects
- # keep a list of backreferences to WeakRef objects referencing them. When a
- # WeakRef is garbage collected, it will remove itself from the backreferences
- # of the object it was referencing.
- #
- # Note: WeakRefs should NOT be stored in savefiles. The finalizers might not be
- # serializable and the list of references is not stored in a persistant way.
- # Also, finalizers are not threadsafe, so if you are insane enough to attempt
- # any kind of multithreading, you would need to extend the methods with some
- # kind of lock or mutex.
- # -----------------------------------------------------------------------------
- # Terms of Use:
- # Free to use for both commercial and non-commercial games. Please give credit.
- # -----------------------------------------------------------------------------
- # Credits:
- # Iavra
- # -----------------------------------------------------------------------------
- # Changelog:
- # - 1.00: First version.
- # - 1.01: Added a hash method to ensure equality in hashs.
- #==============================================================================
- ($imported ||= {})[:iavra_weakref] = true
- module IAVRA
- #============================================================================
- # ▼ IAVRA::WeakRef
- #============================================================================
- class WeakRef
- #==========================================================================
- # A class variable, that is used to keep track of active WeakRef objects
- # and the objects they are referencing.
- #==========================================================================
- @@refs = {}
- #==========================================================================
- # This will be registered as a finalizer for every new WeakRef instance.
- #==========================================================================
- @@finalizer = lambda {|object_id| cleanup(object_id)}
- #==========================================================================
- # When a WeakRef instance is garbage collected, we remove it from our list.
- # Also, we lookup the object it was referencing to and remove the reference
- # from its backreferences.
- #==========================================================================
- def self.cleanup(object_id)
- return if (ref_object_id = @@refs.delete(object_id)).nil?
- ref_object = ObjectSpace._id2ref(ref_object_id) rescue return
- (ref_object.__iavra_backrefs__ || return).delete(object_id)
- end
- #==========================================================================
- # We only store the object id of the referenced object. Also, we store our
- # own object id in its backreferences. Finally, we keep track by updating
- # the class variable and set our finalizer.
- #==========================================================================
- def initialize(ref_object)
- @ref_object_id = ref_object.__id__
- (ref_object.__iavra_backrefs__ ||= []) << object_id
- @@refs[object_id] = @ref_object_id
- ObjectSpace.define_finalizer(self, @@finalizer)
- end
- #==========================================================================
- # We make a lookup on the referenced object and check its backreferences,
- # to make sure that it's still the same. This is done, because object ids
- # may be re-used after an object got garbage collected.
- #==========================================================================
- def object
- obj = ObjectSpace._id2ref(@ref_object_id)
- obj if (obj.__iavra_backrefs__ || []).include?(object_id)
- rescue RangeError
- nil
- end
- #==========================================================================
- # This is mainly for debugging and shows, that we are a reference and what
- # object we are pointing to.
- #==========================================================================
- def inspect
- "<##{self.class.name}: #{(obj = object) ? obj.inspect : "##{@ref_object_id} (not accessible)"}>"
- end
- #==========================================================================
- # Overridden hash method to check for equality when used as a hash key.
- #==========================================================================
- def hash
- object.hash
- end
- end
- end
- #==============================================================================
- # ▼ Object
- # -----------------------------------------------------------------------------
- # I admit, this is lazy, but the cleaner way would be more performance heavy,
- # as i would need to dynamically add instance variable to referenced objects
- # via instance_variable_set and read them via instance_variable_get.
- #==============================================================================
- class Object
- attr_accessor :__iavra_backrefs__
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement