这是@lurker 在问题评论中提到的内容的扩展。无论您使用类变量还是类级实例变量,您都会看到相同的行为。
但是,当您使用这两种类型的变量时,幕后发生的事情是不同的。
案例1:类变量(即@@var
)
类变量的值是在类声明期间设置的,即在读取 Ruby 源代码时只发生一次。
这里有两件事要记住:
- Rails 遵循延迟加载,即它在第一次需要时加载一个类的定义。
- 所以当你第一次点击
url_a
时class A
被加载,即它的源被解析
class B
尚未加载。它稍后会在您点击时加载url_b
。
- 解析 Ruby 源文件时,任何函数之外的所有代码都会立即执行。所以
Base.result =
方法调用是在加载两个类时执行的。
所以步骤的顺序是:
- 在对 的调用中
url_a
,class A
被解析并Base.result = a
设置@@var
为a
- 然后在对 , 的调用中
url_b
被class b
解析并Base.result = b
设置@@var
为b
,并且对于所有后续调用都保持不变。
以下代码片段可能有助于理解第二点:
irb(main):033:0> class ParseTest
irb(main):034:1> @@time_now = Time.now
irb(main):035:1> def speak_time
irb(main):036:2> puts @@time_now.to_s
irb(main):037:2> end
irb(main):038:1> end
=> nil
irb(main):039:0> pt = ParseTest.new
=> #<ParseTest:0x007f80758514c8>
irb(main):040:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):041:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):042:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):043:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):044:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):045:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):046:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):047:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):048:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):049:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):050:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):051:0> class ParseTest2 < ParseTest
irb(main):052:1> @@time_now = Time.now
irb(main):053:1> end
=> "2014-09-18T23:16:41.911+05:30"
irb(main):054:0> pt.speak_time
2014-09-18 23:16:41 +0530
=> nil
irb(main):055:0>
如您所见,在ParseTest
类定义被解析一次后,@@time_now
后续的任何puts
. time 的值是解析源代码时的值。
然而,当我定义了ParseTest2
子类并解析了它的代码时,同一个类变量被赋予了一个新的时间值。当我使用基类的相同旧对象打印它时,会反映这个新值。
这也是您的代码中发生的事情。
案例2:类级实例变量(即@var
在任何实例函数之外的类定义中) 现在,如果您在类定义中(即在任何函数之外)使用实例变量而不是类变量,那将是非常不同的。这种情况可能看起来有点令人困惑,因此请阅读并重新阅读以下代码片段,如果它在第一次让您感到困惑时。
irb(main):089:0> class Base
irb(main):090:1> @time_now = Time.now
irb(main):091:1>
irb(main):092:1* def self.time_now=(time)
irb(main):093:2> @time_now = time
irb(main):094:2> end
irb(main):095:1>
irb(main):096:1* def self.time_now
irb(main):097:2> puts @time_now.to_s
irb(main):098:2> end
irb(main):099:1> end
=> nil
irb(main):100:0> class A < Base
irb(main):101:1> Base.time_now = Time.now
irb(main):102:1> end
=> "2014-09-18T23:33:26.514+05:30"
irb(main):103:0> Base.time_now
2014-09-18 23:33:26 +0530
=> nil
irb(main):104:0> A.time_now
=> nil
irb(main):105:0> A.time_now = Time.now
=> "2014-09-18T23:34:27.093+05:30"
irb(main):106:0> A.time_now
2014-09-18 23:34:27 +0530
=> nil
irb(main):107:0> Base.time_now
2014-09-18 23:33:26 +0530
=> nil
irb(main):108:0>
类级别的实例变量是私有的class
。它不会在继承期间传递/共享。所以继承层次结构中的每个类都有自己的一组实例变量。但是,这些方法确实会被传递,并且这些方法作用于调用它们的类的实例变量。所以根据你调用time_now=
setter方法的类,设置对应的实例变量。
在您的情况下,您总是指的是Base
类的实例变量。因此,与前面案例中描述的相同的一组步骤发生
- 在对,的调用中
url_a
,class A
被解析并Base.result = a
设置@var
为Base
a
- 然后在对 , 的调用中
url_b
被class b
解析并Base.result = b
设置@var
为Base
tob
并且对于所有后续调用仍然如此。