Apple uses bundles to represent apps, frameworks, plug-ins, and many other specific types of content. ——from developer.apple.com
苹果用“包”来表示应用、框架、插件以及其它一些特定类型的内容集合。
关键字:包
/iOS
/Swift
/Bundle
/Framework
/Resource File
/Info.plist
先看代码
开发过程中我们会调用这样的代码:
// Swift 2.2
// Get the app's main bundle
let mainBundle = NSBundle.mainBundle().path(forResource: FILE_NAME, ofType: FILE_TYPE)
// Swift 3.0
// Get the file path of the resource file
let filePath = Bundle.main.path(forResource: FILE_NAME, ofType: FILE_TYPE)
「Bundle」是「Foundation」中定义的类,是用于开发者获取资源文件的一个接口。
了解概念
除了Bundle
表示「包」以外,还有个单词Package
也是「包」,这两者在Apple的定义中拥有不同的含义。
- Bundle:是一个具有标准层级结构的目录,该目录包含可执行二进制代码,以及相关的资源文件。
- Package:是一个以单一文件呈现的目录。
相对与「Package」而言,「Bundle」更像是一个有组织有预谋的东西。
设计总是伴有目的性的,「Package」存在的目的是用来提升用户体验的,而「Bundle」的目的则是用来提升开发者体验的。
围绕提升开发者体验来看,苹果为不同平台不同的内容提供了不同的「Bundle标准」,开发者无需手动去构建一个项目的「Bundle」,通过Xcode创建项目即自动会生成相对应的「Bundle」(通过Makefile打包项目例外)。当然,每个「Bundle」有其必要的组成文件,开发者仅需在项目中添加和修改资源文件,而无需手动管理这些文件,同时在代码中引用这些资源文件也是通过Foundation框架中的Bundle类作为接口获取这些资源文件。
从物理文件的角度来看,一个项目的所有文件包括可执行代码都被按照既定的「Bundle标准」打包好,all in one,简洁明了,便于使用。从操作系统的角度来看,系统(OS X/iOS)自然知道「Bundle」是按怎样的标准打包的,因此可以解析其中的资源文件以及调用包中的可执行程序,这也是为何你双击一个「Bundle」程序就启动的原因,虽然它们从本质上来看都只是文件夹而已。从程序代码的角度来看,所有的资源文件都是按既定标准乖乖呆在某个地方,而开发者并不用关心具体的文件在哪以及如何调用,因为这样的设计必然包含了程序上的接口,也就是Foundation框架中定义的「Bundle」类(在此之前是NSBundle,在Swift3后改成了Bundle),使用Bundle类定义的接口就可以更友好地引用资源文件了。
包结构/Bundle Structure
iOS的包结构如下所示:
MyApp.app
MyApp
MyAppIcon.png
MySearchIcon.png
Info.plist
Default.png
MainWindow.nib
Settings.bundle
MySettingsIcon.png
iTunesArtwork
en.lproj
MyImage.png
fr.lproj
MyImage.png
从以上包结构可以看出,这样设计的目的之一是解决本地化资源文件的引用问题。把本地化资源引用交给系统自动化处理即可,开发者无需关心其细节。
除此之外,每个iOS的「Bundle」都包含了Info.plist文件,这也是「Bundle标准」中定义必须要有的,该文件其实是个配置文件,供操作系统和开发者使用。因为有了这个「Bundle标准」,也才使得可以在Runtime通过「Bundle类」来获取程序相关配置项。
Tip:获取配置项
了解「Bundle」是为了更好地使用「Bundle」,在此之后写「Bundle」相关代码的时候应该能少不少疑惑。
程序的很多配置项都是在Info.plist中定义的,那如何获取相关配置项呢?
let nameSpace = Bundle.main.object(forInfoDictionaryKey: "CFBundleExecutable")
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion")
// 很多代码中会使用 Bundle.main.infoDictionary[CONFIG_NAME]的方法获取配置项,这也是可行的
// 只不过苹果开发文档中更推荐上面代码中的接口
PS:通常在Xcode中查看Info.plist文件显示的并非真实的配置项字符串,查看真实配置项字符串可以通过Ctrl+配置项,然后在弹出菜单中选择Show Raw Keys/Values
,如下图所示:
关于「Bundle」相关API详细使用,可查看官方文档:Bundle Class Reference
总结
说了这么多,总结下就是苹果设计了「Bundle」这个东西来解决来一堆本来开发者需要自行解决的问题,但前提是开发者必须先知道啥是「Bundle」。
「Bundle」其实是iOS开发中比较重要的一个概念,刚开始iOS开发时可能并不会接触到这个概念,或许在copy代码的过程中接触到了,但也没深入理解过。但作为一个程序员而不仅仅是代码的搬运工,理解它并利用好它是我们的职责。所以我抽空上开发者官网简单看了眼这个「Bundle」的概念,然后做了这个记录。