class 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 are 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 are 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 !UNDEF_P(obj) ? 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 keys 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_clear_i, (st_data_t)self);
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(!UNDEF_P(wkmap_lookup(self, key)));
} Возвращает true, если key является ключом в self, и false в противном случае.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.