alias 和无限循环


alias 可以用来别名一个方法,生成其副本留以他用。在我们需要给一个方法增添一些内容,又不想重写一遍不需要改动的部分时,我们就可以用 alias。但是这个机制往往伴随着一个潜在的问题,那就是 alias 的老对头——无限循环。

来看一个简单的例子:

class A
  def fn
    p "instance method `fn'"
  end
  def foo
    alias fn_old fn
    def fn
      fn_old
    end
    fn
  end
end

a = A.new
a.foo
a.foo        # Stack level too deep.

在第二次调用 foo 时发生了栈层次过深的异常。其原理是:在 A#foo 第一次调用、第一次 alias fn_old fn 时,由于 foo 内部的局部方法 fn 还没有定义,所以 alias 拷贝的是实例方法 A#fn 的一份副本;在 A#foo 第二次调用后,由于先前定义的局部方法 fn 已经存在于 A 的实例上下文中,所以第二次 alias 后 fn_old 就拷贝之前定义的局部方法 fn 的一份副本;而这个局部方法 fn 的副本又会去调用 fn_old—一个已经变成了这个局部方法本身副本的别名,于是 fn 调用 fn 自己,产生了没有出口的递归,导致最终抛出 `SystemStackError: Stack level too deep’ 的异常。

总结:两次对同一个方法 alias 同一个别名就会出现这样的情况。脚本编写者在使用 alias 时应尽量使别名不与自己或他人所写脚本的相同方法的别名碰撞,如添加上标识着脚本编写者的前缀: szsu_fn_old。在脚本有可能被重新执行时,使用 Module.method_defined? 等方法可判断出副本方法是否已定义,只有当方法副本没有定义时我们才定义,这样也可以有效地避免方法副本重定义。

8 Comments

  1. Milo
    Posted 2010/08/24 at 10:10 | Permalink | Reply

    这个是什么语言?

    • Posted 2010/08/24 at 10:22 | Permalink | Reply

      这是 Ruby。居然在这里看到你,稀客啊稀客。

  2. Posted 2010/08/24 at 12:58 | Permalink | Reply

    国内访问wordpress大鸭梨,所以就过来吐吐槽 ^_^

    codepad.org 和 fayaa.com不错,前者可以用于保存代码,后者可以生成代码高亮的Html代码,我的代码高亮插件就是这样解决的。

    • Posted 2010/08/25 at 09:00 | Permalink | Reply

      可能也只是部分线路有鸭梨?6R 上有人说通行无阻。

      • DeathKing
        Posted 2010/08/26 at 11:03 | Permalink | Reply

        或许吧,有些时候访问还是比较快的,不过大多数情况大鸭梨。
        现在我看你这个博客就像没有主题一样。

        • Posted 2010/08/27 at 02:27 | Permalink | Reply

          试了一下 codepad.org,果然好用,感谢分享!我没有设置主题是为了简洁,有一种返璞归真的美。另外似乎你博客里我的评论还未通过审核?

  3. 胖达达人
    Posted 2010/08/29 at 13:44 | Permalink | Reply

    (囧,为啥找不到登录)无限循环常用的监控方法也就是defined?,但是经常诡异掉……
    好吧……RSS在哪儿……

    • Posted 2010/08/30 at 00:08 | Permalink | Reply

      II 你也用 WordPress o.o
      Meta 下面有 login 选项,RSS 么……全局的在 https://szsu.wordpress.com/feed/
      用 defined? 等监控有一个问题,就是可能使用到比预期更老的方法别名,比如两次 alias 后,该方法已经被新增了两次内容,但第三次 alias 与第一次 alias 同名,于是变成了在第一次的基础上新增内容,覆盖了第二次新增的内容……

Leave a comment