包罗框架


包罗框架(umbrella framework)是一种公共的系统框架,它含有并且链接着子框架(subframework)和其它由苹果公司所提供的公共框架。子框架是一种公共的(但受限制的)系统框架,它通常被用于打包特定的某项苹果技术,例如Open Transport或QuickDraw。

“包罗(umbrella)”这个词暗示了一个包罗框架包含有所有用于定义一个应用程序环境或系统软件层的技术和API。在用于外部开发人员链接程序的文件和苹果公司用于实现所提供的工程技术之间,包罗框架提供了一个抽象层。子框架的内部组成是会发生变化的。苹果公司采取了适当的机制,用来防止开发人员直接包含或链接子框架。

苹果公司没有把包罗框架推荐给第三方开发人员,而是推荐外部开发人员将他们的框架打包在应用程序中。更多内容请参见“应用程序打包”一章。

本章对各种不同的私有框架和公共框架的类型进行了描述,对包罗框架和子框架进行了定义,举例说明了包罗框架的内部结构,并且提出了链接包罗框架的指导原则。

 

框架的种类


Mac OS X中的主要应用程序环境和系统软件的分层(应用服务层、核心服务层和内核环境层)一样都被打包成包罗框架。要想对这个术语进行定义,需要以下的几个步骤来进行解释和说明。

首先,什么是框架?框架是一种分层结构的目录,它囊括了一个动态共享库以及相应的支持资源。这些资源包扩头文件、参考文档、图像文件和本地化字符串等。“框架”一章对框架进行了具体描述,框架也是一种束的类型,但在某些重要方面它不同于其它束的类型,例如应用程序和插件;关于束的具体信息请参见“束”这一章。

第二,一个框架可以定义为几种类型中的任意一个,或者是“特色类型”。私有框架仅仅被用于内部开发,并且它们的API也向用户公开。按照习惯,它们被放置在系统的、网络的或某个用户的Library目录中的PrivateFrameworks文件夹内,网络的文件夹PrivateFrameworks里,或者是用户的Library目录下;然而,如果需要将框架紧密地与应用程序捆绑在一起,那么通常它们可以被安装在应用程序包中(参见“应用程序打包”)。公共框架将直接交付给用户,它们的API通过其头文件向用户公开。按照习惯,它们被安装在合适的Library位置的Frameworks目录下。

第三,苹果带来的Mac OS X具有三种类型的公共框架:简单类型的公共框架,简单类型的子框架和简单类型的包罗框架。这些框架都被安装在硬盘上的/System/Library/Frameworks目录下。安装在这些目录中的公共框架也可以是简单类型的公共框架,也就是说它既不是包罗框架也不是子框架。但只有对于那些在操作系统的早期版本(例如:早期版本的Mac OS X Server)中已被广泛应用的公共框架,才会是这种情况,Cocoa应用程序环境中的Foundation和Application Kit都属于这一类。

 

包罗框架的用途


一个包罗框架简单地包含并链接着成员子框架和其它公共框架。一个包罗框架包含有所有用于对某个应用程序环境或系统软件层进行定义的技术和API。在外部开发人员用于链接程序的文件和苹果用于实现所提供的工程技术之间,它提供了一个抽象层。

子框架在结构是一个打包了苹果的某一项特定技术的公共框架,例如Quartz或Open Transport。然而,子框架是一种带有限制的公共框架。尽管子框架的API是公开的,但苹果公司采取了适当的机制用来阻碍开发人员直接与子框架相链接(参见“对子框架链接的限制”)。一个子框架通常位于安装在/System/Library/Frameworks目录下的包罗框架中,并且在这个包罗框架中公开了它的头文件(参见“包罗框架的结构”)。

某些包罗框架还包含有其他的包罗框架;Carbon和Cocoa应用程序环境里的框架就是这种包罗框架的典型例子。举例来说,Carbon和Cocoa都(直接或间接)引用并链接了核心服务包罗框架(CoreServices.framework)。进一步,这个包罗框架又引用并链接了例如Core Foudation和Open Transport这些子框架。图8-1对这种关系进行了描述。

图8-1 包罗框架和它的子框架之间的关系

隶属于包罗框架内的子框架的确切构造是会发生变化的。但是通过提供一种间接链接的方式,包罗框架使得开发人员屏蔽了这些变化。苹果可能会在包罗框架内部重新构造子框架,也可能会在子框架内添加、重命名或删除头文件。但这些变化都不会影响到链接着包罗框架的程序。

一个包罗框架的价值在于,当您的程序与它相链接且只与它相链接时,您可以确信在一个特定的应用程序环境或系统软件层中,您可以访问到所有与编程相关的API。包罗框架将许多系统软件不同片断之间的复杂的交叉依赖关系隐藏了起来。因此当您要完成一个特定任务时,您无需知道所必须引入的是哪些框架和哪些库。包罗框架之所以能够用于较快速地创建程序,是因为它包含有一个连同了任何包罗头文件或框架头文件的预编译头文件。

 

链接与包含的指导原则


对于Mac OS X的开发人员来说,包含头文件和链接系统软件的指导原则应该是相当直截了当的:仅仅包含包罗框架的头文件,同时只链接那些对于您所创建的程序来说是合适的包罗框架。

一个包罗框架的头文件包含有它的子框架的框架头文件。(例如在一个子框架中)一个框架头文件包含有所有该框架中的头文件。您绝对不应该直接包含来自子框架中的头文件或者是直接链接它们(事实上,系统也无法允许您这样做)。

在Mac OS X中,包含头文件的一般语法是

#include <Framework/Header.h>

在这条语句中Framework是框架名,Header是头文件名。

注意:对于Objective-C工程来说,#import可能被用来替换#include;它的指示作用和#include是一样的,但是#import能保证同一个文件绝对不会被重复包含多次。

在为Mac OS X开发软件时,用于指定包罗框架所使用的语法也是这种用来指定框架头文件的#include语法。例如,指定一个Carbon包罗框架就是使用了下面的命令:

#include <Carbon/Carbon.h>

然而,为了让开发人员能够将源代码从Mac OS 9移植到Mac OS X,或者移植其它能在两种操作系统上使用的代码,苹果为Carbon开发人员提供了一个用于过度的解决方案。这个可选的“简单头文件(flat header)”允许它们能够继续使用已有的#include命令。/Developer/Headers/FlatCarbon目录下的所有文件都是对应于公共Mac OS 9头文件的遗留文件(stub file)。这些遗留文件为编译器重新指定了适合的包罗框架头文件,或者包含了当此API在Mac OS X上无效时的警告信息。要想使用遗留文件,您必须使用编辑器的-I标志(是大写的I,而不是小写的l)来包含在FlatCarbon文件夹内的头文件:

-I/Developer/Headers/FlatCarbon

确定您包含了MacWindows.h和MacTypes.h这二个头文件。

一旦您只需针对Mac OS X来编译代码,那您就应当使用包含包罗框架的本机语法。(作为这一措施所产生的影响,生成目标程序的时间将被缩短。)也可以条件化您的#include命令,在Mac OS X上创建程序时,它们可以直接包含包罗框架(例如:#include <Carbon/Carbon.h>),而在Mac OS 9上创建程序时,它们可以包含简单头文件(例如:#include < Dialogs.h>)。条件编译将不再需要-I标志。

“Inside Carbon: Carbon Porting Guide(Carbon移植指南)”一书中更为详细的讨论了简单头文件(flat-header)的#include技术。关于包罗框架头文件的更多内容也可参见“包罗框架的结构”

不用担心链接包罗框架并包含成打的头文件会胀满您程序的内存。因为框架的可执行代码是一个动态共享库,只有当它的一个函数或方法被首次调用时,一个子框架的代码才会被装载到内存中。如果您的程序没有使用到子框架,那么它就不会被装载。关于这一主题的更多内容参见“框架”一章的“动态共享库”

 

包罗框架的结构


包罗框架的结构由两方面因素决定。第一个方面的因素是它所包含的头文件的形式。第二个方面的因素是,它作为一个束目录是如何组织其子框架的。

在前面的章节里,所提到的#include例子暗示了由包罗框架所提供的包罗头文件和框架头文件是如何被用来实现该抽象层的。在此重申,用于包含框架头文件和包罗头文件的#include命令的一般语法为:

#include <Framework/Header.h>

按照习惯,框架头文件和包罗头文件有着同样的文件名。

一个包罗头文件包含有它的子框架的框架头文件。举个例子来说,核心服务包罗框架的包罗头文件CoreServices.h所含内容类似于以下内容:

#include<CoreFoundation/CoreFoundation.h>
#include<OT/OT.h>
#include ...

框架头文件包含了用于定义某项特定技术的公共接口的所有头文件。例如,CoreFoundation.h用于Core Foundation子框架,它所包含的内容类似于以下内容:

#include<CoreFoundation/CFBase.h>
#include<CoreFoundation/CFArray.h>
#include<CoreFoundation/CFBag.h>
#include ...

从物理上说,包罗框架含有通过子目录和符号链接来建立其结构的子框架(“符号链接”的机制类似于“替身”)。列表8-1描述了一个假定的框架。(在这个例子中,符号链紧是那些带有“at”符(@)后缀的项目;(它们包含了所引用的路径。)

列表8-1一个包罗框架的结构

Umbrella.framework/
    Headers@ -> Versions/Current/Headers/
    PrivateHeaders@ - > Versions/Current/PrivateHeaders/
    Resources@ -> Versions/Current/Resources/
    Umbrella@ -> Versions/Current/Umbrella
    Versions/
    Frameworks/
SubFW1.framework/
SubFW1@ -> Versions/Current/SubFW1 Headers@ -> Versions/Current/Headers/ PrivateHeaders@ -> /Versions/Current/PrivateHeaders/ Resources@ -> Versions/Current/Resources/ Versions/ SubFW2.framework/ SubFW2@ -> Versions/Current/SubFW2 Headers@ -> Versions/Current/Headers/ PrivateHeaders@ -> /Versions/Current/PrivateHeaders/ Resources@ -> Versions/Current/Resources/ Versions/

包罗框架的每一个子框架都放在Frameworks目录下。由框架的符号链接所引用的Headers目录含有包罗头文件(在上面的例子中是指Umbrella.h)。包罗头文件包含有一个#pragma命令,那是用来告诉编译器子框架位于哪里。

通常,框架的结构需要注意这样几方面的事情:

 

对子框架链接的限制


Mac OS X含有两种机制用于确保开发人员只与包罗框架相链接。当您试图包含子框架头文件时,其中一种机制被触发,而另一种机制则阻止与子框架的链接。

作为一个外部开发人员,如果您尝试链接一个子框架的话,那么链接器将引发链接错误,并显示一则消息来解释为什么出错。举例来说,如果您试图直接与Open Transport相链接(OT.framework),那么链接失败的同时链接器会显示如下消息:“OT.framework is a subframework. Link against the umbrella framework CoreServices.framework instead.(OT.framework是一个子框架。请链接包罗框架CoreServices.framework。)”

如果您试图包含一个在子框架中的头文件,那么您将得到一个编译时错误的消息。包罗头文件和子框架头文件包含有预处理变量和用于预防包含子框架头文件的检查。如果您用一个不合适的#include语句来编译您的工程,那么编译器就会产生一个错误消息。

 

[ 返回 ]