笔记@Bundle Programming Guide
概览
A bundle is a directory with a standardized hierarchical structure that holds executable code and the
resources used by that code.
- bundle是一种标准的目录结构,能够包含可执行程序,以及程序执行所需的资源文件。
- bundle的作用在于规范代码及其资源的位置,便于系统访问。
- .app / .bundle / .framework,这些都是bunle的后缀。
- 缺少
info.plist
的bundle一样可以使用,但会失去某些特性。 - 对于有
info.plist
的bundle来说,CFBundleDisplayName
代表其在操作系统中的显示名,CFBundleName
才是其真正意义上(代码所用)的名称。
Bundle和Package
A package is any directory that the Finder presents to the user as if it were a single file.
- 任意目录,如果Finder将其作为单独文件呈现给用户,那么这个目录就是一个package。
- 用户可以通过
右键--->Show Package Contents
查看package的内容。 - package的作用在于防止用户对于package作出修改,维护package的统一性。
- 有些bundle也是package,例如application,因为用户可以通过
Show Package Contents
查看其内容;而有些不是,例如framework,它的内容可以直接查看。 - iOS的
.ipa
解压缩后,就得到了一个bundle,也是一个package。
Bundle的类型
- application
- framework
- loadable bundle / 含有可以载入的可执行文件,例如OS X的插件,iOS的widget
- document package / 文档包
Bundle的创建
通过xocde模版创建,applicaton / framework / loadable bundle都有自定特定的目录结构。
-
通过shell脚本或手动创建目录结构。
# 例子:通过脚本创建framework目录结构 set -e export FRAMEWORK_LOCN="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework" # Create the path to the real Headers dir mkdir -p "${FRAMEWORK_LOCN}/Versions/A/Headers" # Create the required symlinks /bin/ln -sfh A "${FRAMEWORK_LOCN}/Versions/Current" /bin/ln -sfh Versions/Current/Headers "${FRAMEWORK_LOCN}/Headers" /bin/ln -sfh "Versions/Current/${PRODUCT_NAME}" \ "${FRAMEWORK_LOCN}/${PRODUCT_NAME}" # Copy the public headers into the framework /bin/cp -a "${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/" \ "${FRAMEWORK_LOCN}/Versions/A/Headers"
Bundle结构
iOS Bundle结构
app bundle /
可执行文件
Info.plist
app icon(如有)
app启动图片(如有)
storyboard / nib(如有)
Settins.bundle(用于在系统设置中显示相关内容,如有)
通用资源文件(同本地化资源相对,如音频,视频,数据库文件)
en.lproj...(本地化资源目录)
- 注意,iOS app的通用资源都放在的bundle的根目录下。
framework结构
The structure for frameworks is based on a bundle format that predates OS X and supports the inclusion of multiple versions of the framework’s code and resources in the framework bundle.
- framework的bundle结构不同于app,一个framework能够同时支持库的多个版本。
- 静态库/动态库可以单独使用,也可以打包作为framework使用。
- framework不一定要包含库所暴露的头文件,但包含了用起来就会很方便。
MyFramework.framework/
MyFramework->Versions/Current/MyFramework // 指向最新版本
Resources->Versions/Current/Resources // 指向最新版本
Versions/
Current->A // 指向最新版本
A/
MyFramework
Info.plist
Headers/
MyHeaders.h
Resources/
English.lproj/
InfoPlist.strings
- 注意,framework的通用资源放在Resources目录下。
The bundle’s Versions subdirectory contains the individual framework revisions while symbolic links at the top of the bundle directory point to the latest revision.
- 跟目录下的子目录
Versions
包含库的各个版本,而根目录下的链接都指向这个字目录下的最新版本的相关内容。
Loadable Bundle
- OS X的plugin / widget都属于loadable bundle;用于打包资源的bundle也属于此列;所以,这种bundle可能包含可执行文件,也可能不包含。(虽然文档明确表示iOS不支持loadable bundle,但资源包类型的bundle也可以被使用)
MyLoadableBundle.bundle
Contents/
Info.plist
MacOS/
MyLoadableBundle // 可执行文件,资源包没有这个目录
Resources/
Pic.jpg
Beauty.png
en.lproj/
MyLoadableBundle.nib
InfoPlist.strings
jp.lproj/
MyLoadableBundle.nib
InfoPlist.strings
- 其结构同OS X应用bundle基本一致。
- 注意,通用资源放在Resources目录下。
Document Package
- 没看
访问Bundle内容
Bundle对象
The main bundle is the bundle that contains the code and resources for the running application.
main bundle
是当前运行的可执行文件及其资源所在的bundle。-
NSBundle的创建方式:
- [NSBundle mainBundle]。
- bundleWithPath: 根据绝对路径创建。
- bundleWithIdentifier: 通过
CFBundleIdentifier
找到已经载入内存的bundle。 - allFrameworks / allBundles: 返回相关的所有bundle。
- bundleForClass: 某个类(的二进制文件)所在的bundle。
资源搜索模式
系统在bundle中搜索资源时,分为两步:
-
确定使用哪个本地化文件夹内的资源:
- 根据系统的
首选语言顺序
寻找相应地本地化文件夹?.lproj
; - 如果没找到,则根据
CFBundleDevelopmentRegion
来确定选定本地化文件夹。
- 根据系统的
-
然后按照以下优先级搜索:
- 通用资源目录
- 特定地区资源目录
- 特定语言资源目录
- 开发语言资源目录
- 注意,不会搜索上述目录下的二级目录,详见
pathForResource:ofType:
的文档。 - 如果通用目录和特定语言目录有
同名资源
,则后者永远不会被找到。
根据iOS设备类型区分资源
-
资源命名方式
<basename> <device> .<filename_extension>
basename
: 用来在代码中访问资源文件filename_extension
: 文件类型扩展名device
: ~ipad / ~iphone-
例子
// MyImage~ipad.png / MyImage~iphone.png UIImage* anImage = [UIImage imageNamed:@"MyImage.png"];
从其他Bundle中加载资源
-
从main bundle中其他bundle加载资源,可以分为四步:
- 在main bundle中找到特定bundle。
- 载入bundle,即创建bundle对象。
- 从bundle中获取资源路径。注意,如果资源位于次级目录,则必须指明路径。
- 通过路径创建对象。
例如,如下代码从app bundle根目录下的另一个bundle中获取一张图片。
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 在main bundle中找到特定bundle
NSString *sampleBundlePath = [[NSBundle mainBundle] pathForResource:@"SampleBundle.bundle" ofType:nil];
// 2. 载入bundle,即创建bundle对象
NSBundle *sampleBundle = [NSBundle bundleWithPath:sampleBundlePath];
// 3. 从bundle中获取资源路径
// 注意这里的图片位于通用资源目录下的Images二级目录,相对路径要明确这种关系
NSString *pic1Path = [sampleBundle pathForResource:@"pic1.png" ofType:nil];
// 4. 通过路径创建对象
UIImage *image = [UIImage imageWithContentsOfFile:pic1Path];
}
通用资源目录
-
所谓通用资源目录,起初我以为是由Info.plist中的
CFBundlePackageType
决定的,可能的类型包括:- APPL--->iOS应用
- FMWK--->framework
- BNDL--->lodable bundle
但试着删除Info.plist后,系统依然遵循搜索模式,而非从bundle根目录开始搜索。
接着,我试着打乱bundle下的目录顺序,例如将Contents和Resources目录删除,将所有资源文件任意放置,发现不论如何尝试,都无法使用
pathForResource:ofType:
找到资源。-
最后,通拼接
bundle路径+资源路径
的方式,找到了资源。代码如下:- (void)viewDidLoad { [super viewDidLoad]; // bundle路径 NSString *sampleBundlePath = [[NSBundle mainBundle] pathForResource:@"AlipaySDK.bundle" ofType:nil]; // 拼接资源路径,bar.png放置于bundle根目录下 NSString *pic1Path = [NSString stringWithFormat:@"%@/bar.png", sampleBundlePath]; // 根据路径创建图片 UIImage *image = [UIImage imageWithContentsOfFile:pic1Path]; }
拾遗
-
决定哪些资源文件应该加入bundle
选定target--->Build Phases--->Copy Bundle Resources: 选择需要加入的资源文件 // 如果需要保留目录结构,则应将资源文件连同文件夹一起拖入项目,并选中Added Folders项下的Create folder references
-
本地化目录命名方式
language_region.lproj // 其中,_region可以不要
If you used custom subdirectories in your bundle to organize resource files, you can speed up the search by providing the name of the subdirectory that contains the desired file.
-
如果要从bundle中的某个次级目录搜索资源,那么必须指定这个次级目录的路径,否则可能找不到。
// Posters是bundle中的二级目录 UIImage* anImage = [UIImage imageNamed:@"Posters/MyImage.png"];
-
iOS app的生成过程:
build App Target--->app bundle--->.ipa