关于Ruby中自取消定义undef_method方法时运行机制的技术问询
最近我刚发现Ruby里有个特殊方法叫undef_method,它的作用就是字面意义上的——取消已存在方法的定义。
比如,你可以用它取消Integer类的<方法:
class Integer undef_method :< end begin puts 1 < 2 rescue Exception => exception puts exception puts 1.0 < 2 end
运行结果是:
undefined method `<` for an instance of Integer true
这很好理解,毕竟Ruby是高度面向对象的语言,所有东西都是对象,包括类型系统本身。
但更有意思的是,你甚至可以取消undef_method自己的定义,这样之后就没法再取消Integer的<方法了:
class Module undef_method :undef_method end class Integer undef_method :< end begin puts 1 < 2 rescue Exception => exception puts exception puts 1.0 < 2 end
因为undef_method本身已经被取消定义了,这段代码会报错:
temp.rb:6:in `<class:Integer>': undefined method `undef_method' for class Integer (NoMethodError) undef_method :< ^^^^^^^^^^^^ from temp.rb:5:in `<main>'
现在问题来了:Ruby是怎么做到在执行取消undef_method自身定义的代码时,还能正常运行undef_method的呢?
毕竟Ruby是解释型语言,不像编译型语言那样会先加载所有方法再执行代码,照理说如果在执行过程中把方法取消定义,后续应该没法调用才对,但这里偏偏能成功执行undef_method :undef_method这行代码,这是为什么?
解答
其实这背后的关键在于Ruby的方法查找时机和执行流程:
调用动作先于方法移除
当Ruby执行class Module; undef_method :undef_method; end这行代码时,undef_method的调用请求会先触发方法查找——此时这个方法还存在于Module类的方法表中,所以能正常找到并触发执行。方法执行的逻辑是修改Module的方法表,移除自身的条目,但这个修改要等当前的方法调用完全执行完毕才会生效,不会影响正在进行的这次调用。单条方法调用的原子性
尽管Ruby是逐行解释执行,但对单条方法调用的处理是原子性的:从查找方法、入栈执行,到完成方法逻辑(修改方法表),整个过程是连贯完成的。在这个过程中,方法本身始终处于可用状态,只有当这次调用彻底结束后,后续的代码再尝试调用undef_method时,才会因为方法表中已经没有这个条目而报错。方法表的动态修改时机
undef_method的作用是修改类的方法存储结构(方法表),但这种修改是“事后生效”的。正在执行的undef_method调用已经拿到了方法的执行入口,不受后续方法表修改的影响——就像你已经打开了一扇门走进房间,之后再把门拆掉,也不会影响你在房间里完成该做的事。
简单来说:调用undef_method来取消它自己的定义时,调用动作发生在方法被移除之前,整个调用过程完整执行完毕后,这个方法才会从方法表中消失,后续的调用才会失败。
备注:内容来源于stack exchange,提问作者S.Y. Kim

