类 ObjectSpace::WeakKeyMap
一个 ObjectSpace::WeakKeyMap
是一个键值映射,它对键持有弱引用,因此当没有更多引用时,它们可以被垃圾回收。
与 ObjectSpace::WeakMap
不同
-
对值的引用是强引用,因此它们在映射中时不会被垃圾回收;
-
键是按值比较的(使用
Object#eql?
),而不是按标识比较; -
只有可垃圾回收的对象可以用作键。
map = ObjectSpace::WeakKeyMap.new val = Time.new(2023, 12, 7) key = "name" map[key] = val # Value is fetched by equality: the instance of string "name" is # different here, but it is equal to the key map["name"] #=> 2023-12-07 00:00:00 +0200 val = nil GC.start # There is no more references to `val`, yet the pair isn't # garbage-collected. map["name"] #=> 2023-12-07 00:00:00 +0200 key = nil GC.start # There is no more references to `key`, key and value are # garbage-collected. map["name"] #=> nil
(注意,GC.start
这里仅用于演示目的,可能并不总是导致演示结果。)
该集合特别适用于实现轻量级值对象的缓存,以便仅将每个值表示的副本存储在内存中,但不再使用的副本将被垃圾回收。
CACHE = ObjectSpace::WeakKeyMap def make_value(**) val = ValueObject.new(**) if (existing = @cache.getkey(val)) # if the object with this value exists, we return it existing else # otherwise, put it in the cache @cache[val] = true val end end
这将导致 make_value
始终为相同属性集返回相同的对象,但不再需要的 value 不会永远停留在缓存中。
公共实例方法
map[key] → value 点击切换源代码
如果找到,返回与给定 key
关联的值。
如果找不到 key
,则返回 nil
。
static VALUE wkmap_aref(VALUE self, VALUE key) { VALUE obj = wkmap_lookup(self, key); return obj != Qundef ? obj : Qnil; }
map[key] = value → value 点击切换源代码
将给定的 value
与给定的 key
关联
对 key
的引用是弱引用,因此当没有其他对 key
的引用时,它可能会被垃圾回收。
如果给定的 key
存在,则用给定的 value
替换其值;顺序不受影响
static VALUE wkmap_aset(VALUE self, VALUE key, VALUE val) { struct weakkeymap *w; TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w); if (!FL_ABLE(key) || SYMBOL_P(key) || RB_BIGNUM_TYPE_P(key) || RB_TYPE_P(key, T_FLOAT)) { rb_raise(rb_eArgError, "WeakKeyMap must be garbage collectable"); UNREACHABLE_RETURN(Qnil); } struct wkmap_aset_args args = { .new_key = key, .new_val = val, }; st_update(w->table, (st_data_t)&key, wkmap_aset_replace, (st_data_t)&args); RB_OBJ_WRITTEN(self, Qundef, key); RB_OBJ_WRITTEN(self, Qundef, val); return val; }
clear → self 点击切换源代码
删除所有映射条目;返回 self
。
static VALUE wkmap_clear(VALUE self) { struct weakkeymap *w; TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w); st_foreach(w->table, wkmap_free_table_i, 0); st_clear(w->table); return self; }
delete(key) → value 或 nil 点击切换源代码
delete(key) {|key| ... } → object
删除给定 key
的条目并返回其关联的值。
如果没有给出块并且找到了 key
,则删除条目并返回关联的值
m = ObjectSpace::WeakKeyMap.new key = "foo" # to hold reference to the key m[key] = 1 m.delete("foo") # => 1 m["foo"] # => nil
如果没有给出块并且没有找到 key
,则返回 nil
。
如果提供了一个块,并且找到了key
,则忽略该块,删除该条目,并返回关联的值。
m = ObjectSpace::WeakKeyMap.new key = "foo" # to hold reference to the key m[key] = 2 m.delete("foo") { |key| raise 'Will never happen'} # => 2
如果提供了一个块,但没有找到key
,则将key
传递给该块,并返回该块的返回值。
m = ObjectSpace::WeakKeyMap.new m.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
static VALUE wkmap_delete(VALUE self, VALUE key) { struct weakkeymap *w; TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w); VALUE orig_key = key; st_data_t orig_key_data = (st_data_t)&orig_key; st_data_t orig_val_data; if (st_delete(w->table, &orig_key_data, &orig_val_data)) { VALUE orig_val = (VALUE)orig_val_data; rb_gc_remove_weak(self, (VALUE *)orig_key_data); ruby_sized_xfree((VALUE *)orig_key_data, sizeof(VALUE)); return orig_val; } if (rb_block_given_p()) { return rb_yield(key); } else { return Qnil; } }
getkey(key) → existing_key or nil 点击切换源代码
如果存在,则返回现有的相等键,否则返回nil
。
这可能对实现缓存很有用,这样程序中所有地方都只使用某个对象的副本。
value = {amount: 1, currency: 'USD'} # Now if we put this object in a cache: cache = ObjectSpace::WeakKeyMap.new cache[value] = true # ...we can always extract from there and use the same object: copy = cache.getkey({amount: 1, currency: 'USD'}) copy.object_id == value.object_id #=> true
static VALUE wkmap_getkey(VALUE self, VALUE key) { struct weakkeymap *w; TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w); st_data_t orig_key; if (!st_get_key(w->table, (st_data_t)&key, &orig_key)) return Qnil; return *(VALUE *)orig_key; }
inspect → new_string 点击切换源代码
返回一个新的String
,其中包含有关该映射的信息。
m = ObjectSpace::WeakKeyMap.new m[key] = value m.inspect # => "#<ObjectSpace::WeakKeyMap:0x00000001028dcba8 size=1>"
static VALUE wkmap_inspect(VALUE self) { struct weakkeymap *w; TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w); st_index_t n = st_table_size(w->table); #if SIZEOF_ST_INDEX_T <= SIZEOF_LONG const char * format = "#<%"PRIsVALUE":%p size=%lu>"; #else const char * format = "#<%"PRIsVALUE":%p size=%llu>"; #endif VALUE str = rb_sprintf(format, rb_class_name(CLASS_OF(self)), (void *)self, n); return str; }
key?(key) → true or false 点击切换源代码
如果key
是self
中的一个键,则返回true
,否则返回false
。
static VALUE wkmap_has_key(VALUE self, VALUE key) { return RBOOL(wkmap_lookup(self, key) != Qundef); }