关于可加载的Bundle

Posted by CoderLeonidas on March 19, 2020

关于可加载的Bundle

可加载的Bundle(Loadable)是可执行代码和相关资源的包,可以在运行时加载。这种灵活性允许您设计高度模块化、可定制和可扩展的应用程序。阅读本文后,您将了解可加载的包是如何构造的,以及何时使用它们。

可加载bundle的介绍

OS X在整个系统中使用一个名为bundle的目录结构来打包可执行代码和相关资源。bundle目录本质上是将一组资源打包在一个独立的包中。资源包括接口生成器nib文件、图像、声音和本地化字符串等。因为代码和相关资源位于文件系统中的一个位置,所以安装、卸载和其他形式的软件管理都比较容易。

bundle有几种类型:应用程序、框架和可加载包(applications, frameworks, and loadable bundles)。本文档专门处理可加载的bundle,这些bundle包含可以在运行时加载的代码,将您从静态编译的许多限制中解放出来。围绕可加载的bundle构建应用程序将为您带来许多好处:

  • 您可以延迟加载代码直到需要它,在某些情况下,在不再需要的时候卸载代码。
  • 您可以将应用程序划分为可以独立编译的各个部分,这样就可以更容易地划分开发工作,并相互测试不同组件的不同版本
  • 可以通过设计插件体系结构来扩展应用程序。通过这种方式,您或第三方开发人员可以轻松地添加新特性,而无需重新编译整个应用程序,甚至无需访问其源代码。

卸载仅限于不使用Cocoa运行时环境的应用程序。在撰写本文时,Objective-C运行时不支持卸载Objective-C符号,因此一旦加载了Cocoa包,就不能卸载。

OS X定义扩展.bundle来标识可加载的bundle。您还可以为特定类型的bundle定义自己的扩展(extension)和关联的图标。

内核扩展是一种可加载bundle的类型,系统bundle例程可以正确识别和处理它(尽管它们的内部结构与其他可加载bundle不同)。这些bundle的扩展名为.kext。内核管理器将内核扩展声明为一种文档类型,并动态地将它们加载到内核环境中。本文档不进一步讨论内核扩展。要了解更多信息,请参阅Darwin文档。

有关bundle的更多信息,请参见Bundle Programming Guide。有关OS X如何加载和执行代码的详细信息,请参阅Mach-O运行时架构。

什么时候用可加载bundle

基本上,可加载的bundle允许您在运行时加载代码和绑定符号。实际上,它们可以帮助您满足三个相关的需求

  • 1 延迟(“懒惰”)链接和加载
  • 2 代码组件的模块化
  • 3 应用程序可扩展性

延迟链接和加载

大型、复杂的应用程序执行各种任务。通常,用户不需要在给定的使用会话中执行所有可用的任务。这意味着应用程序代码块(有时是重要的代码块)被加载到内存中,但从未使用过。

通过将应用程序划分为由逻辑相关的代码组成的可加载包,可以避免不必要的代码加载。通过同时包含这些代码片段的访问函数或方法,您可以无缝地引用这些对象,而无需直接检查是否加载了代码,这些检查在访问方法中隐式地进行。

延迟加载可以采用分层的形式,在顶层有大量的应用程序块,下面有更具体的特性。特定功能通常采用插件的形式,如应用程序可扩展性中所述。

代码模块化

有几种方法可以将应用程序模块化为逻辑上独立的代码组件。模块化代码的基本方法是将代码分割成多个源文件,所有重要的应用程序都是这样构建的。下一个级别的复杂性是一个framework,它包含可执行代码、头文件和目录包中的其他资源。通过一些额外的工作,您还可以使用可加载的bundle作为模块化的单元。

虽然并不是每种情况都需要通过可加载bundle进行模块化,但是通过增加额外的工作,您可以获得许多好处。当代码库被分割成多个bundle时,每个bundle都可以独立于其他bundle进行开发和测试,这甚至比framework更重要。因为bundle包含动态加载的可执行代码,所以应用程序的其他部分可能不知道函数地址、对象地址,甚至不知道引用的对象属于哪个类,只要它知道足够多的类信息就可以使用它。不同版本的不同代码组件可以在不重新编译应用程序的情况下进行测试,您可以通过在Finder中拖动图标来组合不同的组件。

应用扩展性

可加载bundle的大多数用法源于对应用程序可扩展性的需求。如果希望应用程序可扩展,可以在组织内部或由第三方开发人员定义插件体系结构。插件体系结构定义了一个接口,通过该接口,一个正确构造的可加载bundle(称为插件)可以添加功能。插件的示例有屏幕保护程序模块、首选项窗格、界面构建器调色板、Adobe Photoshop图形过滤器和iTunes音乐可视化工具。

注意事项

虽然Cocoa和Core Foundation都在这方面提供了丰富的API支持,但是使用可加载的bundle并不是没有代价的。如果您试图决定是否使用可加载bundle,请记住以下成本:

  • 可加载的bundle与应用程序bundle和framework不同,必须在运行时显式加载。如果您需要代码可重用性而不是动态加载,那么framework通常是更好的选择。
  • 在Cocoa中,可加载的bundle不能用作大部分代码的内存管理方案,因为Cocoa bundle不能被卸载。
  • 定义插件架构需要仔细验证插件模块
  • 允许在应用程序中使用外部插件代码会带来稳定性和安全性问题。

但是,如果您的应用程序需要延迟加载,那么使用可加载bundle可以提供比framework更多的动态模块性或可扩展性。

可加载bundle的内部结构

bundle目录包含资源和可执行代码的层次结构,每个都位于适当的位置。典型的bundle目录层次结构如清单1所示。

Listing 1 The directory layout of a typical loadable bundle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- MyLoadableBundle
    Contents/
        Info.plist
        MacOS/
            MyLoadableBundle
        Resources/
            Lizard.jpg
            MyLoadableBundle.icns
            English.lproj/
                MyLoadableBundle.nib
                InfoPlist.strings
            Japanese.lproj/
                MyLoadableBundle.nib
                InfoPlist.strings

每个bundle目录都包含一个项,即Contents目录。Contents包含两个文件,Info.plistPkgInfo,以及MacOS目录(其中包含可执行代码)和Resources目录(其中包含所有非代码资源)。更复杂的包可能包含额外的目录,如FrameworksPlugInsSharedFrameworksSharedSupport,它们支持成熟的应用程序包所支持的所有功能。

info.plist文件,或信息属性列表,是一个XML属性列表,包含关于绑定包的键值对信息。系统例程允许bundle可执行文件在运行时读取这些属性。您也可以在信息属性列表中存储任何应用程序定义的数据。Xcode提供了一个用于编辑信息属性列表的用户界面,默认情况下包括所有必需的键。

信息属性列表键可以通过向包含在资源目录中的语言目录(如Japanese.lproj)中包含的InfoPlist.strings 文件,添加相应的条目来本地化。

可加载bundle的最相关的信息属性列表键是:

  • CFBundleExecutable:执行文件的名称,通常与不带扩展名的bundle目录相同
  • CFBundleIdentifier:bundle的全球唯一标识符,按反向dns顺序,如com.apple.screensaver.Flurry
  • CFBundleName:bundle的短显示名,用作人类可读的标识符(应该本地化)
  • CFBundleDisplayName:bundle的显示名称,用于在Finder中表示bundle,除非被用户覆盖(应该本地化)

MacOS目录包含一个可加载包的Mach-O可执行代码。该目录中的可执行文件的名称通常与不带扩展名的bundle目录相同,并且与information属性列表中的CFBundleExecutable键的值相同。某些类型的bundle可能缺少可执行代码。但是,可加载的bundle、应用程序和大多数其他类型的bundle本质上都需要它。

Resources目录包含与包关联的所有资源,如声音、图像、接口生成器nib文件和本地化的字符串。本地化的资源包含在language.lproj目录中。如果您愿意,可以在Resources中创建子目录来组织资源类型。

有关信息属性列表的详细信息,请参见Information Property Lists.

参考文章

About Loadable Bundles