Spec-Zone.ru › Ruby 3.4

класс 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 всегда будет возвращать один и тот же объект для одного и того же набора атрибутов, но значения, которые больше не нужны, не будут храниться в кэше вечно.

Открытые методы экземпляра

map[key] → value
Исходный код
static VALUE
wkmap_aref(VALUE self, VALUE key)
{
    VALUE obj = wkmap_lookup(self, key);
    return !UNDEF_P(obj) ? obj : Qnil;
}

Возвращает значение, связанное с данным key, если оно найдено.

Если key не найдено, возвращает nil.

map[key] = value → 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;
}

Связывает данное value с данным key.

Ссылка на key является слабой, поэтому, когда нет других ссылок на key, он может быть удалён сборщиком мусора.

Если данный key существует, заменяет его значение на данное value; порядок не изменяется

clear → self
Исходный код
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.

delete(key) → value or nil
delete(key) {|key| ... } → object
Исходный код
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"
getkey(key) → existing_key or nil
Исходный код
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
inspect → new_string
Исходный код
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>"
key?(key) → true or false
Исходный код
static VALUE
wkmap_has_key(VALUE self, VALUE key)
{
    return RBOOL(!UNDEF_P(wkmap_lookup(self, key)));
}

Возвращает true если key является ключом в self, иначе false.

Ruby Core © 1993–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API