编程语言及工具
什么是「动态语言」?这个概念其实没有一个明确的定义。基本上它是一个程度的度量。这个程度就是该语言的 runtime 到底使用多少 bookkeeping 数据。
读过《Design and Evolution of C++》的人一定知道 C++ 这个变态的 zero-bookkeeping 原则。任何 C++ 语言的高层概念,其实都已经在编译阶段被剥离掉了。从最后的目标代码中你很难再看出这是一种高级语言。当然,其代价就是程序员必须理解为什么 C++ 不能实现某些功能。而且必须从机器的角度去理解。
动态语言其实就是一个不断添加 bookkeeping 的过程。
比如说,C++ 中为了实现多态还是不得不有一个中间机制,这就是虚表。但是你很难说虚表就是一个 bookkeeping 结构。因为它太简单了。而 Objective-C 就大大增加了对成员函数调用的 bookkeeping 机制。因为如此,所以 Objective-C 对 action-message 的实现就简单多了,因为你可以判断一个成员函数是否存在。而且也可以在不确定对象类型的情况下,指定一个方法作为回调函数。
C++ 的另一个问题,内存管理,根本原因在于其对象引用采用 raw pointer 机制。Objective-C 并没有改善这一点。但是也并非全无改善,在 Objective-C 里,一个 pointer 几乎永远必须指向 NSObject,而这个东西是有引用基数的。当然,它并没有完全解决 over-release 或者 use-after-release 的问题。到了 Java,Python,Lua 这样的语言,raw pointer 就完全消失了。
而 C++ 的内存管理,除了 heap 就只有一个借助 CPU stack 管理的栈。在动态语言里,就要考虑 lexical scope 的表现,这就需要更多的 bookkeeping。这点 Objective-C 也并没有实现。
语法的处理,在 C++ 中是完全在 runtime 之前进行。而在 Python,Lisp,Lua 这样的语言中是有 eval 这样的机制存在的。
所以,Objective-C 是比 C 和 C++ 拥有更多动态特性,而比 Lua,Lisp 缺乏一些动态特性的语言。至于题目中进行比较的 Python,也只能说是个更少缺乏动态特性的语言。Python 缺乏 lexical scope,也缺乏对 continuation 的支持。它的 stack 借助 CPU stack(当然有一个 stackless-Python,不过非官方),相比之下,Lua 为了支持 coroutine,Lisp 为了 full-continuation,都是自行维护 VM stack 的。Python 的 bookkeeping 与 Lua 和 Lisp 比起来也是不够的。
即运行时再决定对象的类型。简单说就是id类型,任何对象都可以被id指针所指,只有在运行时 才能决定是什么类型。像内置的明确的基本类型都属于静态类型(int、NSString等)。静态类型在编 译的时候就能被识别出来。所以,若程序发生了类型不对应,编译器就会发出警告。而动态类型就编译器编译的时候是不能被识别的,要等到运行时(run time),即程序运行的时候才会根据语境来识别。所以这里面就有两个概念要分清:编译时跟运行时。
基于动态类型,在某个实例对象被确定后,其类型便被确定了。该对象对应的属性和响应的消息也被完全确定,这就是动态绑定。比如我们一般向一个NSObject对象发送-respondsToSelector:或者 -instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应的类 的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态地向类或者实例添加新的方 法,也即类的实现是可以动态绑定的;isKindOfClass也是一样的道理。
所谓动态加载就是我们做开发的时候icon图片的时候在Retina设备上要多添加一个张@2x的图片,当设备更换的时候,图片也会自动的替换。
什么是 bookkeeping 信息:Bookkeeping 就是 source code 里的信息用 declarive 的方式来保留在目标码中。
这里举一个所有语言都会舍弃的 bookkeeping 信息,就是 local variable name。Compiler 或者 interpreter 在遇到一个 local variable 的时候,一定要给它分配一个寄存器或者 stack entry(其实寄存器也就是 stack entry 的一种,见我的 blog 《什么是寄存器》)。所以在 runtime 时 variable name 就成了多余的。所以几乎所有语言,不管是 native 还是 byte code,都会舍弃 local name。
再比如说 raw pointer,就是说编译之后的代码里没有一个固定的 field 来说明一个 value 到底是不是 pointer。一个 value 是不是 pointer 完全靠引用它的代码本身来解释。当然你去分析目标码本身可能会得出「这是一个 pointer」的结论,但这是从 imperative code 中分析的来的,而不是从一个 field 中得到的明确的 declartive 信息。
全部0条评论
快来发表一下你的评论吧 !