класс 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 всегда будет возвращать один и тот же объект для одного и того же набора атрибутов, но значения, которые больше не нужны, не будут храниться в кэше вечно.
Публичные методы экземпляра
static VALUE
wkmap_aref(VALUE self, VALUE key)
{
VALUE obj = wkmap_lookup(self, key);
return obj != Qundef ? obj : Qnil;
} Возвращает значение, связанное с данным key, если оно найдено.
Если key не найдено, возвращает nil.
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;
} Связывает данное value с данным key
Ссылка на key является слабой, поэтому, когда нет других ссылок на key, он может быть собран сборщиком мусора.
Если данный key существует, заменяет его значение на заданное value; порядок не меняется
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;
} Удаляет все элементы карты; возвращает self.
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;
}
} Удаляет запись для данного 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_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;
} Возвращает существующий эквивалентный ключ, если он существует, в противном случае возвращает 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_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;
} Возвращает новую String содержащую информацию о карте:
m = ObjectSpace::WeakKeyMap.new m[key] = value m.inspect # => "#<ObjectSpace::WeakKeyMap:0x00000001028dcba8 size=1>"
static VALUE
wkmap_has_key(VALUE self, VALUE key)
{
return RBOOL(wkmap_lookup(self, key) != Qundef);
} Возвращает true если key является ключом в self, в противном случае false.
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.