Кое-что о модуле ObjectSpace в Ruby
января 28, 2011 | Published in Ruby, Основы | 6 Comments
Ruby и 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.
Это, наверное, самый бесполезный метод. Дело в том, что он запускает сборщик мусора, который и так всегда запущен. Или я что-то не так понимаю? Прошу отписаться в комментариях!
Лучшая благодарность автору — ваши комментарии!
января 28, 2011 at 06:35 (#)
Если я правильно понимаю, GC в руби не «всегда запущен», а запускается периодически, иначе бы он требовал слишком много ресурсов. а GC.start вызывает его неотложный запуск прямо сейчас.
января 28, 2011 at 12:58 (#)
Спасибо Andrew! Мне друг рассказывал, что в Ruby GC работает по определенным событиям, например, когда память заканчивается и т.д., но он не был уверен, что это именно в Ruby, а в интернете я информации не нашел. Не подскажите ссылок, где можно об этом побольше узнать?
января 28, 2011 at 15:15 (#)
Вот же
января 29, 2011 at 11:05 (#)
Хорошие подробности про организацию памяти, отслеживание объектов и (с 33 слайда) Garbage Collector:
января 29, 2011 at 12:14 (#)
Спасибо за ссылки. Буду штудировать =)
февраля 2, 2011 at 09:16 (#)
Ну всё таки не такой уж и бесполезный, по крайней мере ребята из 37signals нашли пользу: