技术开发 频道

C++开发者:Objective-C的历史和原理

  【IT168技术】老读者都知道我这些天花了很长时间写Objective-C的代码。现在我已经完成一个Smalltalk的编译器,这个编译器可以在对象与Objective-C的对象相关联的地方产生代码。同时,这篇文章也可以给那些熟悉C++的人一个关于Objective-C大概的介绍。

  Objective-C是为C提供给类Smalltalk语义学的最小集合的拓展而设计的。它也包括了类Smalltalk的语法,这可以轻松的看出部分代码是纯C的,并且这部分包括Objective-C的拓展。

  与C++不同,Objective-C是C的一个超集。每一个C程序都是一个有效的Objective-C程序。所有Objective-C中新关键字都以@未前缀,但在C的标识符这并不是有效的。

  运行时系统

  Objective-C的第一个具体实现是一个预处理器,它使Objective-C转化为C。语言的动态特性由一个运行时库提供。现代的编译器不用C作为中间语言, 但他们对运行时库提供动态特性有着相同的约定。

  在Objective-C,对象都是C结构体,只是这些结构体第一个元素是一个指向另一个用来代表类的C结构体。准确的来说在运行时这个类结构有些变化,但至少包含查找方法的信息要求、实例变量的布局和其他一些元数据。

  因为Objective-C是C的超集,并且运行时库的所有特性(包括类结构)对C是公开的, 这就意味着Objective-C对编程人员完全公开对象模型。这在Objective-C并不奇怪。当你在C++调用一个方法时,编译器通过虚表进行一些复杂操作来找到正确的代码运行。在Objective-C,如下:

  调用一个C函数来查找实现方法的函数(通过GNU运行时)

  调用一个查找方法的C函数并且在一次运算中调用(通过Apple运行时)

  这就意味着你可以编写纯C代码与Objective-C对象交互。它也允许你在你需要的情况下绕过一些动态行为。

      

  一些历史

  19世纪80年代初,Objective-C由Brad Cox发明,起初是作为对C微小集合的拓展来支持Smalltalk风格的对象。它从面向对象预编译项目中脱颖而出,这个项目与Objective-C有着相似的语义但的确是些复杂的语义。

  最初的实现和Objective-C的商标由NeXT持有并全部应用在新的操作系统中。NeXT在Objective-C中创建了一个GUI工具集和一个接口创建器。这被广泛认为第一个商业快速应用开发(RAD)工具。好的一面就是接口创建器并不保存接口描述—存储序列化对象。这种方式可以保持一个模式-视图-控制器清楚的分离并且以可装载模式存储控制器和视图。

  早在19世纪90年代,NeXT和Sun对NeXT环境分别构建了便携版本。而NeXT初始系统在一些地方使用了纯C语言,新版本定义了两个框架:Foundation和AppKit.Foundation定义了核心功能,包括集合类,字符串和其他一些东西。AppKit包含了GUI框架。这两个都定义在OpenStep规格说明中,而这个说明也在NeXT、Sun和随后的一些GNU项目中实施。在收购NeXT之后,苹果公司将OpenStep实施改名为”Cocoa”。因为OpenStep基于早期的NeXT设计,在OpenStep的所有的类都以NS为前缀。

  GCC支持Objective-C的历史颇有些趣味性色彩。起初的实现是由NeXT所编写,而保存在一个分离的库,这样使用者可以链接他们的GCC的版本。通过没有和GCC的分离,NeXT希望规避GPL。这个小把戏没有成功,而且NeXT最后被要求公开源代码。然而,并没有要求公开运行时库的源代码,这就意味着编译器在其他平台都没有作用。Richard Stallman 编写了一个新的版本实际上是用来取代NeXT运行时。这后来被更新的版本取代。

  在GCC中,Objective-C现在的代码基于你使用的学科。NeXT[FS:PAGE]保留着自己的版本,这个版本因为没有分层而且都在单一源文件中所以仅仅勉强维持, 并且只支持NeXT运行时。GCC包含了一个不用维护的,基于NeXT代码但以#ifdefs标记的GNU运行时。

  曾经GCC支持Objective-C并试图以复制NeXT环境开始。GNUstep项目最终从这里发展并且实施几乎所有的OpenStep和Cocoa附加的一些。它也包括NeXT的接口创建器的取代:GNU对象关系模型(GORM)。

  Objective-C的一些语法

  在Objective-C中语法的加法被设计为强调各种面向对象代码的语义。在C++中,你可以这样做:

  doSomething();这是调用一个C函数,调用一个静态的C++成员函数通过现前的对象传递作为一个隐含的参数,或者调用一个可以在子类中实现虚函数, 通过现

  前的对象作为一个隐含的参数。换一种方法,你也可以这样做:

  a.doSomething();这是调用一个C函数指针,它是一个结构体的域,调用一个定义在类上的静态成员函数其中你认为a是类的一个实例,或者一个定义在类上的虚方法其中a是一个真正实例。

  相反,在Objective-C中看起来象C其实就是C。在Objective-C中调用一个方法的等价源于Smalltalk语法:

  [a doSomething];方括号用来包装类Smalltalk信息发送语法。我将回过头来讲诉发送消息和调用函数的区别。当你有一个有很多参数的方法时,这些参数在Objective-C都有自己的名字,象这样:

  [aDictionary setObject:@”a string” forKey:@”a key”];对那些熟悉C语法的人这看起来非常奇怪,但它意味着你花很少时间去考虑参数的顺序。而且如果你先前不知道aDictionary是类的一个实例的话这也使代码易读—你知道这种方法传递一个对象和一个键,而且你只知道这些。

  在后台,这转化为下面这些:

  SEL sel = sel_get_uid(“setObject:forKey:”);

  IMP method = obj_msg_lookup(aDictionary->isa,sel);

  method(aDictionary, sel, @”a string”, @”a key”);

  我将回过头来讲诉sel是什么。当这起作用时,这并没有象发生的那样—当模块加载的时候通过运行时库的初始化填充sel的值。NeXT/Apple将后面两步合二为一如下:

  obj_msgSent(aDictionary, sel, @”a string”, @”a key”);速度稍微快些,但使实现优化类似不确定性内联变得稍微困难一些。因为这些是C函数,他们也不特殊——你可以在你自己的代码中调用他们。有一件有趣的事情,在LLVMC语言家族前后树是一个复写者,它包含Objective-C源文件并编译成C源文件以兼容在NeXT运行时库。

  对象模型

  术语面向对象是由Alan Kay在19世纪60年代提出的。他一直和一个在Xerox PARC的小组从事开发Smalltalk语言,并且这种语言作为编程模型思想的具体体现。

  面向对象编程最基础的想法是通过简单的计算机模型来传递消息。这非常有别于C++方式,C++有点类似面向对象,但它基于函数和数据结构结合的思想。

  在C++中,对象是一系列与函数有关的结构,而这些函数或是静态的或是虚的。一个静态函数语义上等价于一个C函数,这个函数隐含的第一个参数包含着对象。这完全是对C的无指针拓展,因为下面两个是相等的。

  //C++

  object->function();

  //C

  function(object);C++版本更多的作为代表,但并没有任何语义上区别。

  C++在虚成员函数的格式上也支持一些变通。在先前的例子里如果函数是个虚函数,真正的函数调用取决于对象的类。如果它是静态的,调用函数取决于调用者所考虑类的对象。

  在Objective-C,没有等价于静态成员函数。如果你想要这个功能, 你只能使用C函数。Objective-C中的方法与C++中的虚方法非常相似,但有一些重要的区别。首先是语法不同:

  [object message];发送命名消息到对象。执行代码作为这个行为的结果,而这完全取决于对象的类。这一点非常不同于C++的等价:在C++中,这种机制稍[FS:PAGE]微取决于所考虑类的对象。

  假设你有一个类继承于四个类。A是父类,B和C是A的子类,D是B的子类。在C++中,除了A其他的类都实现虚函数doSomething(),你可以称这种方式叫模板。考虑下面:

  object.doSomething();如果你认为对象是类B或D的,并且实际上是类D的对象。实现声明通过D被调用。如果你认为是类C的,将通过C的实现来调用。如果你认为是A的一个实例,你需要尝试两个显式动态映射并且看看哪一个起作用,或是否使用同一个模板。

  如果你在Objective-C中有相同的类约定,用B、C和D实现一个doSomething方法,你可以尝试如下:

  [object doSomething];如果你认为对象是B类型的,但其实是C类型的,doSomething方法依然会被调用。如果你认为是类A的实例,编译时警告弹出,A的实例没有回应一个doSomething消息。如果真的是B、C或是D的实例,它将在运行时起作用,但它是A的一个实例,运行时异常将会弹出。

  多继承在Objective-C中没有实现,但相对于C++来说远远没必要。在C++中,方法的查找基于继承链,所以你无法使用两个类继承,除非他们有共同的父类。在Objective-C中,你可以完全使用两个类在回应相同消息的条件下进行交互。

  总结

  我们已经浏览了Objective-C的历史和语言背后的核心思想。我们也发现了一些语言的重要部分:给对象发消息。在第二部分,我们会看到更多的核心语法。

0
相关文章