类 ObjectSpace::WeakKeyMap

一个 ObjectSpace::WeakKeyMap 是一个键值映射,它对键持有弱引用,因此当没有更多引用时,它们可以被垃圾回收。

ObjectSpace::WeakMap 不同

(注意,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 点击切换源代码

如果keyself中的一个键,则返回true,否则返回false

static VALUE
wkmap_has_key(VALUE self, VALUE key)
{
    return RBOOL(wkmap_lookup(self, key) != Qundef);
}