class User @var = 1 end
Ожидаемое поведение
Переменные экземпляра работают так, как от них ожидают в обычных ситуациях:
class A def set_a( v ); @a = v; end def get_a; return @a; end end o1 = A.new o1.instance_variables # => [] o1.get_a # => nil o1.set_a 123 o1.instance_variables # => ["@a"] o1.get_a # => 123 o2 = A.new o2.get_a # => nil
Переменная появляется в экземпляре после ее установки, дальше все работает как по маслу — она видна только в своем экземпляре.
Значение по-умолчанию
Неожиданности с переменныой экземпляра обычно начинаются когда нужно установить значение ее по-умолчанию:
class B attr_accessor :b # вместо set_b, get_b из вышестоящего примера @b = 1 end o1 = B.new o1.b # => nil o1.instance_variables # => [] o1.b = 2 o1.b # => 2 o1.instance_variables # => ["@b"]
Оп-па. А где же наше значение по-умолчанию? Почему переменной вообще нет в обьекте с самого начала?
Такие переменные называются Class-Level Instance Variable (как это по-русски «переменные экземпляра Class-уровня»?)
Классы это объекты.
На Ruby, что любой код выполняется в контексте текущего обьекта (экземпляра класса), текущий объект всегда доступен из псевдо-переменной self. Это базовые истинины. Но попорбуйте ответить на вопрос, в каком конексте (объекте) выполняется строка 4 и строка 2?
1: class User 2: @b = 1 3: def a 4: @a = 1 5: end 6: end
Строка 4 выполнится в экземпляре класса User, как мы и ожидаем, в объекте который вы создадите через User.new
А строка 2 выполняется в экземпляре класса Class, в объекте который описывает класс User. Этот объект создатся сразу после чтения интерпретатором класса User.
Само слово User это не что иное как консанта, значение которой объект типа Class. Этот объект описывает наш класс User. Именно к этому объекту относится переменная @b.
o1 = B.new o1.b # => nil o1.instance_variables # => [] B.instance_variables # => ["@b"]
На самом деле она всегда была там. Не в классе B и не в экземпляре класса B, а в экземпляре класса Class хранимом в константе B.
Применение
Что нам дают переменные экземпляра Class-уровня?
Они могут работать как личные переменные класса. В отличии от настоящих переменных класса, которые доступных во всех потомках и экземплярах под одним именем, наша переменная принадлежит только тому классу в контексте которого была объявлена.
class B @b = 1 def self.set_b( v ); @b = v; end def self.get_b; @b; end end B.get_b # => 1 B.set_b 2 B.get_b # => 2 class B1 < B; end B1.get_b # => 1 B1.set_b 3 B1.get_b # => 3 B.get_b # => 2
То есть при каждом наследовании от класса, в котором была объявлена такая переменная будет создаваться новая независимая. Сравните с поведением переменной класса:
class C @@c = 1 def self.set_c( v ); @@c = v; end def self.get_c; @@c; end end C.get_c # => 1 C.set_b 2 C.get_b # => 2 class C1 < C; end C1.get_c # => 2 C1.set_c 3 C1.get_c # => 3 C.get_c # => 3 class C2 < C; @@c = 4; end C.get_c # => 4 C1.get_c # => 4 C2.get_c # => 4
Accessors
Такие переменные с помощью акцессоров создаются следующим образом:
class A class << self attr_accessor :a end # Вместо этого такая строка: self.class.attr_accessor :a # невозможна только потому, что attr_accessor это private method :( end A.instance_variables # => ["@a"] A.class_variables # => [] A.a # => nil A.a = 123 A.a # => 123
Обратите внимание что рельсовый cattr_accessor :a создаст методы с такими же именами, только обращаться они будут к переменной класса.
class User cattr_accessor :a end User.instance_variables # => [] User.class_variables # => ["@@a"]
Материалы по теме:
railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
wiseheartdesign.com/articles/2006/09/22/class-level-instance-variables/
snippets.dzone.com/posts/show/4382
Уважаемые читатели, если не трудно, накидайте сюда еще ссылок по теме, чтобы можно было использовать этот материал как учебный по теме.
ссылка на оригинал статьи http://habrahabr.ru/post/111781/
Добавить комментарий