为什么说oc是动态语言,原因分析

为什么说oc是动态语言,原因分析,第1张

  什么是「动态语言」?这个概念其实没有一个明确的定义。基本上它是一个程度的度量。这个程度就是该语言的 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 的问题。到了 JavaPython,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 比起来也是不够的。

  1.动态类型:

  即运行时再决定对象的类型。简单说就是id类型,任何对象都可以被id指针所指,只有在运行时 才能决定是什么类型。像内置的明确的基本类型都属于静态类型(int、NSString等)。静态类型在编 译的时候就能被识别出来。所以,若程序发生了类型不对应,编译器就会发出警告。而动态类型就编译器编译的时候是不能被识别的,要等到运行时(run time),即程序运行的时候才会根据语境来识别。所以这里面就有两个概念要分清:编译时跟运行时。

  2.动态绑定:

  基于动态类型,在某个实例对象被确定后,其类型便被确定了。该对象对应的属性和响应的消息也被完全确定,这就是动态绑定。比如我们一般向一个NSObject对象发送-respondsToSelector:或者 -instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应的类 的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态地向类或者实例添加新的方 法,也即类的实现是可以动态绑定的;isKindOfClass也是一样的道理。

  3.动态加载:

  所谓动态加载就是我们做开发的时候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 信息。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/dianzi/2717051.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-08-17
下一篇 2022-08-17

发表评论

登录后才能评论

评论列表(0条)

保存