在2001年非常有趣的post中,Allen Wirfs-Brock解释了如何在不重新启动(本机)堆栈的情况下实现块闭包。
从他揭露的许多想法中,有一个我不太了解的想法,我认为在这里提出这个想法是个好主意。他说:
任何在块的生命周期内永远不能分配的变量(例如,封闭方法和块的参数)不需要放在环境中,如果相反,变量的副本放在闭包中已创建
有两件事我不确定我是否理解得很好:
问题1必须有另一个原因。否则,我不会看到收益(与实施优化的成本相比。)
对于问题2,取一个在方法中而不是在块中分配的非参数。为什么存储在堆栈中的oop在块的生命周期内保持不变?
我想我知道Q2的答案:因为块的执行不能与方法的执行交织在一起,即,当块存在时,封闭的上下文不会运行。但是,当块处于活动状态时,是否有任何方法可以临时修改堆栈?
答案 0 :(得分:3)
感谢@ aka.nice的评论,我在Clement Bera的帖子中找到了两个问题的答案,其阅读既愉快又澄清。
对于Q1,我们首先要说Allen的注释意味着只读变量的副本可以放在块的堆栈中,就像它是块的本地临时值一样。这样做的优点只有在块外部定义并在其中使用的所有变量永远不会写入块中时才能实现。在这种情况下,不需要创建环境数组并发出任何prolog或epilog来处理它。
访问堆栈变量的机器代码等同于访问环境变量所需的机器代码,因为第一个使用[ebp + offset]
来解决位置,而第二个使用[edi + offest]
,{{1}已经设置为指向环境数组(Clement的符号中的edi
。)因此,如果某些环境变量并非所有环境变量都是只读的,则没有任何好处。
Clement的优秀博客也回答了第二个问题。是的,还有另一种方法可以在块的堆栈中打破原始变量和它的副本之间的同步:调试器(就像aka.nice会告诉我们的!)如果程序员在封闭的上下文中修改变量,调试器将需要检测操作并更新副本。如果程序员修改了块的堆栈中保存的副本,则相同。
我很高兴我决定在这里发布这个问题。我从aka.nice和Clement Bera那里得到的帮助,以及一些人通过电子邮件发给我的评论,对我的理解有很大的帮助。
最后一句话。 Wirfs-Brock声称避免方法上下文的具体化是强制性的。我倾向于同意。但是,如果具体化遵循轻量级模式,则可以更好地实现对这些数据结构的许多重要操作。更准确地说,在调试时,您可以使用指向本机堆栈的“查看器”对这些上下文建模,并使用两个索引来分隔与分析中的激活相对应的部分。这既高效又干净,两种技术的结合可以带来世界上最好的,因为你可以立刻拥有速度和表现力。 Smalltalk很棒。