我将使用Kernel#set_trace_func 方法向您解释幕后发生的事情。先看下面的代码和输出:
trace = lambda do |event,file,line,id,binding,klass|
p [event,File.basename(file),line,id,binding,klass]
end
set_trace_func trace
class MyClass
def self.bar;end
def one
def two
end
end
end
obj = MyClass.new
obj.one
obj.instance_eval do
def three
end
end
输出:
-----------------
----------------
-----------------
-----------------
-----------------
----------------- # part A
["c-call", "test.rb", 9, :singleton_method_added, #<Binding:0x83ab2b0>, BasicObject]
["c-return", "test.rb", 9, :singleton_method_added, #<Binding:0x83aaeb4>, BasicObject]
["line", "test.rb", 10, nil, #<Binding:0x83aab80>, nil]
["c-call", "test.rb", 10, :method_added, #<Binding:0x83aa900>, Module]
["c-return", "test.rb", 10, :method_added, #<Binding:0x83aa07c>, Module]
----------------------------- # part B
["line", "test.rb", 16, nil, #<Binding:0x83a976c>, nil]
["c-call", "test.rb", 16, :new, #<Binding:0x83a9488>, Class]
["c-call", "test.rb", 16, :initialize, #<Binding:0x83a90a0>, BasicObject]
["c-return", "test.rb", 16, :initialize, #<Binding:0x83a8e20>, BasicObject]
["c-return", "test.rb", 16, :new, #<Binding:0x83a8b28>, Class]
---------------------------
---------------------------
--------------------------- # part C
["c-call", "test.rb", 11, :method_added, #<Binding:0x83a7de0>, Module]
["c-return", "test.rb", 11, :method_added, #<Binding:0x83a79f8>, Module]
--------------------------- # part D
["line", "test.rb", 18, nil, #<Binding:0x83a7034>, nil]
["c-call", "test.rb", 18, :instance_eval, #<Binding:0x83a6c10>, BasicObject]
["line", "test.rb", 19, nil, #<Binding:0x83a65f8>, nil]
["c-call", "test.rb", 19, :singleton_method_added, #<Binding:0x83a61d4>, BasicObject]
["c-return", "test.rb", 19, :singleton_method_added, #<Binding:0x83a5ef0>, BasicObject]
["c-return", "test.rb", 18, :instance_eval, #<Binding:0x83a5d4c>, BasicObject]
说明:
看看A部分下面的5行。它只是告诉我们 Ruby 何时会在一个类中找到 def 关键字,它会将该方法作为实例方法添加到该类中。这是通过调用钩子方法Module#method_added 来完成的。 part C下面的两行也有同样的解释。
现在obj.instance_eval {..} 内部发生了什么?
好的,如果您查看 part D 下面的行,这将被清除。从最后一行看,first 和 second 行。在 instance_eval 块内,def third 导致 third 添加为对象 @ 的 singleton_method 987654334@,通过调用钩子方法BasicObject#singleton_method_added。
MRI 就是这样写的。