动态机制

在编程历史中的某一个时期,一个程序会使用多少内存是在源代码编译和链接的时候就决定了的,既不会增加,也不会缩小。

现在看来,很明显,这是一个严重的局限因素。它不仅仅限制了程序的结构,程序的功能,还限制了程序的设计和编程技术的进步。动态分配内存的函数(例如malloc)的出现使这些限制不再存在。

之所有成为限制因素是因为程序只能根据编译期和链接期从程序员的源代码中获得的信息做出决定,而不是根据程序的运行环境。

尽管动态分配机制消除了静态内存分配这种限制,但是许多类似的限制仍然存在。例如,编译时期类型匹配,程序的边界地址在链接时期就必须确定。应用程序的每个部分必须组织成一个单独的可执行文件。程序运行时不能加入新的模块和新的类型。

Objective-C基本上克服了这些限制从而使得程序尽可能的灵活。Objective-C将很多编译期和链接时需要作的决定挪到了运行时来作,目的就是让程序根据用户运行程序的环境来决定应该发生什么事情而不是根据语言和编译器,链接器的需要。

在面向对象编程中,有三种类型的动态机制:
动态类型识别,直到运行时才决定一个对象的类别。
动态绑定,直到运行才决定调用哪个方法。
动态加载,运行时加入新的模块。
本节包含如下内容:

动态类型识别
动态绑定
动态加载

动态类型识别

通常,如果您在不能转换的类型之间互相赋值,编译器可能会给出这样的警告:
incompatible types in assignment
assignment of integer from pointer lacks a cast



类型检查有时候是有用的,但是它经常和您从多态中获得的好处冲突,尤其是每个对象的类型必须在编译时确定。

例如,假设您现在需要向一个对象发送start消息。和其它数据元素一样,对象也是一个变量。假如变量的类型必须在编译期确定,这就不可能让运行时的因素决定什么类型的对象将会赋给该变量。如果代码中变量的类型是确定,start消息所调用的方法也就确定了。

从另一方面说,如果变量的类型能在运行时决定,那么任何类型的对象都可以赋给该变量,从而start消息所调用的方法也随着变量在运行时类型的不同而不同,并产生不同的结果。

动态类型识别不仅仅是动态绑定(随后将进行介绍)的基础。动态类型识别容许对象之间的关系到运行期才决定,而不用在设计时确定。例如,一个消息可能在参数中传递一个对象而不指明它的类型,然后消息接收者发送消息给该对象,甚至不用考虑对象的类型。因为消息接收者利用传递过来的对象来完成一部分工作,所以在某种意义上,消息接收者的行为是可以由传递的对象的类型来决定的。
动态绑定

在标准C语言中,您可以声明一些功能相近的函数,例如标准的字符串比较函数:
int strcmp(const char *, const char *); /* case sensitive */
int strcasecmp(const char *, const char *); /*case insensitive*/




并声明一个具有相同参数和返回类型的函数指针如下:
int (* compare)(const char *, const char *);




然后,您可以等到运行时再决定将哪个函数赋给这个指针,
if ( **argv == 'i' )
compare = strcasecmp;
else
compare = strcmp;



并通过函数指针调用字符串比较函数:
if ( compare(s1, s2) )
...




以上和面向对象编程的动态绑定很相似,都是到程序运行时才决定调用的方法。

尽管不是所有的面向对象语言都支持动态绑定,但是动态绑定通常可以通过消息来实现。您不需要象上面一样声明一个函数指针然后给它赋值,也不用给每个方法都取个不同的名字。

消息并不是直接地调用方法的。每个消息都会关联到一个方法调用。消息机制将会检查消息接收者的类型,并且在消息接收者的类的实现中查找对应消息的方法实现。当这一切在运行时完成时,方法和消息就是动态绑定的。如果是在编译期完成,就是静态绑定。

后期绑定:虽然有些面向对象的编程语言(尤其是C++)需要在源代码中静态地指定消息接收者的类型,但是消息接收者可以是其指定类型的对象,也可以其子类的对象。因此,编译器并不能确定消息所调用的方法。这种情况下,有两种选择,一种是简单地将消息和方法按照指定的消息接收者的类型绑定,一种是晚些时候解决这个问题。C++中的选择是将这些方法(成员函数)声明为虚拟函数。一般称这为“后期绑定”而不是“动态绑定”。和动态绑定发生在运行时不同,后期绑定有严格的类型限制。正如现在所讨论的(以及objective-C中所实现的),动态绑定没有限制。

即使没有动态类型识别,动态绑定也是可行的,但是这没有什么意义。如果消息接收者的类能被编译器所确定,则动态绑定没有任何好处,因为编译器链接的方法和运行时绑定的没有任何区别。

然而,如果消息接收者的类型是动态的,则编译器不可能在编译的时候就能链接到正确的方法,只有在运行时,消息接收者的类型已知的情况下,才可能确定应该调用的方法。因此,动态类型识别催生了动态绑定。

动态类型识别使得消息的结果随消息接收者类型的不同而不同成为可能。运行时的因素从而能影响消息接收者的选择和消息的输出。

动态类型识别和动态绑定使得您在代码里可以发送消息一个还未确定的对象。如果该对象的类型可以到运行时再确定,其它的程序员就可以自由的设计自己类和数据类型,并且响应您所发送的消息,只要你们对消息协议而不是数据类型达成一致。

注意: 动态绑定在Objective-C随处可见,您无须刻意安排,您的代码也无需为此作特别的改动。

动态加载

历史上,在大部分运行环境中,程序必须被链接成一个执行文件,程序运行时,执行文件会一次性载入到内存中。

面向对象的编程环境允许一个可执行的程序分成几个文件。程序可以在运行后按照需要动态的加载和链接不同的文件。用户的操作将决定哪个文件会被载入到内存中。

一般来说,只有程序的必要部分才会在程序被启动时加载,其它的模块则随用户的请求而加载。没有被加载的模块并不占用系统内存。

动态加载有一些非常有意思的好处。例如,整个程序不需要一次开发完成。您可以分批的发布您的软件,并且每次更新一部分。您可以在程序中提供很多类似的工具—用户可以选择需要的工具并且只有该工具被会加载。

目前来说,动态加载最重要的好处也许是一个程序的可扩展性。您可以允许其它的人对您的程序定制。您需要作的就是在您的程序中提供一个框架让其它的程序员来实现,并且在运行时发现这些实现并动态的加载他们。例如,任何人都可以定制Interface Build的插件,Interface Build也能自动加载这些插件。

动态加载面临的主要问题时如何使新载入的部分和程序已经运行的部分能够工作,尤其是这俩部分由不同的程序员开发。然而,在面向对象环境中,大部分问题都不成为问题,因为面向对象中的代码按逻辑模块的方式来管理,而且接口和实现分离。当类被动态加载时,它不可能破坏程序已经在运行的部分。每个类都封装了自己的实现并且有独立的名字空间。

此外,动态类型识别和动态绑定让其它程序员设计的类能够和您的程序一起工作。一旦类被动态加载,则和其它已经在运行的类没有什么区别。您的代码中可以无差别的给这些类的对象发送消息,无需关心其它程序员实现的到底是什么类,唯一需要的是你们在通信的协议上达成一致。

加载和链接: 动态加载也被叫做动态链接。程序的各部分被链接在一起,并在启动时按需读入到内存中。动态加载一般指的程序的各部分动态链接在一起,并且在运行时动态地载入到内存中的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值