1require "delegate" 2 3# Weak Reference class that allows a referenced object to be 4# garbage-collected. 5# 6# A WeakRef may be used exactly like the object it references. 7# 8# Usage: 9# 10# foo = Object.new # create a new object instance 11# p foo.to_s # original's class 12# foo = WeakRef.new(foo) # reassign foo with WeakRef instance 13# p foo.to_s # should be same class 14# GC.start # start the garbage collector 15# p foo.to_s # should raise exception (recycled) 16# 17# == Example 18# 19# With help from WeakRef, we can implement our own redimentary WeakHash class. 20# 21# We will call it WeakHash, since it's really just a Hash except all of it's 22# keys and values can be garbage collected. 23# 24# require 'weakref' 25# 26# class WeakHash < Hash 27# def []= key, obj 28# super WeakRef.new(key), WeakRef.new(obj) 29# end 30# end 31# 32# This is just a simple implementation, we've opened the Hash class and changed 33# Hash#store to create a new WeakRef object with +key+ and +obj+ parameters 34# before passing them as our key-value pair to the hash. 35# 36# With this you will have to limit your self to String key's, otherwise you 37# will get an ArgumentError because WeakRef cannot create a finalizer for a 38# Symbol. Symbols are immutable and cannot be garbage collected. 39# 40# Let's see it in action: 41# 42# omg = "lol" 43# c = WeakHash.new 44# c['foo'] = "bar" 45# c['baz'] = Object.new 46# c['qux'] = omg 47# puts c.inspect 48# #=> {"foo"=>"bar", "baz"=>#<Object:0x007f4ddfc6cb48>, "qux"=>"lol"} 49# 50# # Now run the garbage collector 51# GC.start 52# c['foo'] #=> nil 53# c['baz'] #=> nil 54# c['qux'] #=> nil 55# omg #=> "lol" 56# 57# puts c.inspect 58# #=> WeakRef::RefError: Invalid Reference - probably recycled 59# 60# You can see the local variable +omg+ stayed, although it's reference in our 61# hash object was garbage collected, along with the rest of the keys and 62# values. Also, when we tried to inspect our hash, we got a WeakRef::RefError, 63# this is because these objects were also garbage collected. 64 65class WeakRef < Delegator 66 67 ## 68 # RefError is raised when a referenced object has been recycled by the 69 # garbage collector 70 71 class RefError < StandardError 72 end 73 74 @@__map = ::ObjectSpace::WeakMap.new 75 76 ## 77 # Creates a weak reference to +orig+ 78 # 79 # Raises an ArgumentError if the given +orig+ is immutable, such as Symbol, 80 # Fixnum, or Float. 81 82 def initialize(orig) 83 case orig 84 when true, false, nil 85 @delegate_sd_obj = orig 86 else 87 @@__map[self] = orig 88 end 89 super 90 end 91 92 def __getobj__ # :nodoc: 93 @@__map[self] or defined?(@delegate_sd_obj) ? @delegate_sd_obj : 94 Kernel::raise(RefError, "Invalid Reference - probably recycled", Kernel::caller(2)) 95 end 96 97 def __setobj__(obj) # :nodoc: 98 end 99 100 ## 101 # Returns true if the referenced object is still alive. 102 103 def weakref_alive? 104 !!(@@__map[self] or defined?(@delegate_sd_obj)) 105 end 106end 107 108if __FILE__ == $0 109# require 'thread' 110 foo = Object.new 111 p foo.to_s # original's class 112 foo = WeakRef.new(foo) 113 p foo.to_s # should be same class 114 ObjectSpace.garbage_collect 115 ObjectSpace.garbage_collect 116 p foo.to_s # should raise exception (recycled) 117end 118