Кое-что о модуле ObjectSpace в Ruby

января 28, 2011  |  Published in Ruby, Основы  |  6 Comments

rubyRuby и ObjectSpace

В Ruby существует такой магический модуль как ObjectSpace. Он содержит весьма интересные, но редко применяемые методы, о которых и пойдет речь в этой статье.

Метод ObjectSpace.each_object

ObjectSpace.each_object это наверное самый полезный и самый используемый метод из всех методов, которые содержатся в ObjectSpace. Могущество этого метода заключается в том, что он позволяет в реальном времени редактировать объекты, причем это можно делать не с каким-либо определенным объектом, а со всем множеством экземпляров определенного класса.


class Blog

  attr_accessor :blog, :link, :author

  def initialize (blog, link, author)
    @blog   = blog
    @link   = link
    @author = author
  end

  def self.find_blog_by_author_name(aname)
    blog = false
    ObjectSpace.each_object(Blog) do |b|
      b.author == aname ? blog = b : blog
    end

    return blog
  end
end

rubydev = Blog.new('RubyDev', 'http://rubydev.ru', 'Vladimir Melnik')
rubyinside = Blog.new('RubyInside', 'http://rubyinside.com', 'Peter Cooper')
ykblog = Blog.new('Yehuda\'s blog', 'http://yehudakatz.com', 'Yehuda Katz' )

fblog  =  Blog.find_blog_by_author_name('Vladimir Melnik')

Теперь в переменной fblog у нас содержится та ссылка на тот же объект, что и в переменной rubydev.

Вас наверняка не впечатлил этот пример использования и предрекая недовольства я приготовил еще один пример использования. Воспользуемся техникой MonkeyPatching ‘а:


class Examp
  def self.obj_count
    count = 0
    ObjectSpace.each_object(self) do |b|
      count += 1
    end

    return count
  end
end

a = Examp.new
b = Examp.new
c = Examp.new

puts Examp.obj_count #=> 3

Самописный метод obj_count , как не сложно догадаться, предназначен для подсчета количества экземпляра класса. Вы можете сами придумать множество возможных применений для each_object, самым полезным из которых, наверняка, окажется добавление нового метода ко всем уже созданным экземплярам, или только к некоторым выбранным. Привожу пример:


class Name

  attr_accessor :name

  def initialize (name)
    @name = name
  end

  def self.add_specific_to_s
    ObjectSpace.each_object(Name) do |n|
      def n.to_s
        "#{self.name.upcase}"
      end
    end
  end
end

v = Name.new('Vladimir')

puts v #=> #

v.class.add_specific_to_s

puts v.to_s #=> VLADIMIR

Легким движением руки эти штаны превращаются в шорты мы добавляем метод to_s, который позволяет в удобоваримом виде вывести объект на печать, для всех объектов нашего класса. Правда, здорово? Рекомендую поиграть с этим методом, это может показаться вам весьма забавным!

Метод ObjectSpace.count_objects

Честно говоря, я не понимаю, зачем этот метод и я ни разу не встречал кода, где бы он использовался, однако, раз уж я взялся писать обзор модулю ObjectSpace, то придется о нем упомянуть. Все, что делает этот метод — это возвращает хэш с информацией об объектах. Документации по этому методу я не нашел, поэтому обойдусь скудным примером:


puts ObjectSpace.count_objects 
# => {:TOTAL=>9548, :FREE=>5072, :T_OBJECT=>7, :T_CLASS=>382, :T_MODULE=>20, :T_FLOA=>7, :T_STRING=>2523, :T_REGEXP=>9, :T_ARRAY=>254, :T_HASH=>8, :T_BIGNUM=>4, :T_FILE=>4, :T_DATA=>180, :T_COMPLEX=>1, :T_NODE=>1058, :T_ICLASS=>19}

У метода count_object есть один необязательный аргумент — хэш, в который будет записан результат.


h = Hash.new
ObjectSpace.count_object(h)

Где это может применяться — не имею понятия. Буду благодарен, если кто-то поделится этой информацией в комментариях.

Метод ObjectSpace._id2ref

Это достаточно интересный метод, его использования я так же не видел, однако могу себе представить ситуации, где этот метод может пригодиться. Метод _id2ref используется для преобразования ID объекта в ссылку на этот объект. Пример использования:


s = "Hello!"
r = s.object_id #=>6525600
puts ObjectSpace._id2ref(r) #=> Hello!

Все просто!

Метод ObjectSpace.define_finalizer

Метод define_finalizer можно назвать действительно магическим! Как вам известно, Ruby динамический язык (некоторые даже называют его сверхдинамическим) благодаря тому, что интерпретатор берет на себе множество задач, которые раньше лежали на программисте, происходит упрощение и ускорение разработки, а также уменьшение количества ошибок. Так уж повелось, что в Ruby нет метода-деструктора объекта. Метод-деструктор — это метод, который выполняется после того, как объект отработал и будет уничтожен, эдакий предсмертный вопль. Несмотря на то, что такого метода нету, Ruby, как язык безграничных возможностей, позволяет самостоятельно создавать методы-деструкторы. Создание методов — деструкторов берет на себя метод define_finalizer. Пример использования:


class Examp

  def initialize 
    puts "I was born!"
    ObjectSpace.define_finalizer(self, self.method(:finalize).to_proc)
  end

  def finalize(id)
    puts "#{id} is dead!"
  end

end

a = Examp.new 
#=> I was Born!
# 6525627 is dead!

Первый аргумент метода define_finalizer — это объект, для которого будет создан деструктор, второй аргумент — это блок кода, который будет выполнен при уничтожении объекта. Пользы от этого, честно говоря, не так уж много, так как вы не можете сами уничтожить объект, этим занимается интерпретатор, а точнее та то, что называется Garbage Collection (GC) — сборщик мусора.

Метод ObjectSpace.undefine_finalizer

Как не сложно догадаться из названия тот метод удаляет деструктор из объекта переданного в качестве аргумента. Пример использования:


ObjectSpace.undefine_finalizer(a)

Теперь мы не увидим сообщения: «6525627 is dead!».

Метод ObjectSpace.garbage_collect

Из документации он также известен как: GC.start, gc.garbage_collect.
Это, наверное, самый бесполезный метод. Дело в том, что он запускает сборщик мусора, который и так всегда запущен. Или я что-то не так понимаю? Прошу отписаться в комментариях!

Лучшая благодарность автору — ваши комментарии!

Tags: , ,

Responses

  1. says:

    января 28, 2011 at 06:35 (#)

    Если я правильно понимаю, GC в руби не «всегда запущен», а запускается периодически, иначе бы он требовал слишком много ресурсов. а GC.start вызывает его неотложный запуск прямо сейчас.

  2. admin says:

    января 28, 2011 at 12:58 (#)

    Спасибо Andrew! Мне друг рассказывал, что в Ruby GC работает по определенным событиям, например, когда память заканчивается и т.д., но он не был уверен, что это именно в Ruby, а в интернете я информации не нашел. Не подскажите ссылок, где можно об этом побольше узнать?

  3. says:

    января 28, 2011 at 15:15 (#)

    Вот же

  4. Meredian says:

    января 29, 2011 at 11:05 (#)

    Хорошие подробности про организацию памяти, отслеживание объектов и (с 33 слайда) Garbage Collector:

  5. admin says:

    января 29, 2011 at 12:14 (#)

    Спасибо за ссылки. Буду штудировать =)

  6. GarPit says:

    февраля 2, 2011 at 09:16 (#)

    Ну всё таки не такой уж и бесполезный, по крайней мере ребята из 37signals нашли пользу:

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.