筆記@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是一種標準的目錄結構,能夠包含可執(zhí)行程序,以及程序執(zhí)行所需的資源文件。
- bundle的作用在于規(guī)范代碼及其資源的位置,便于系統(tǒng)訪問。
- .app / .bundle / .framework,這些都是bunle的后綴。
- 缺少
info.plist的bundle一樣可以使用,但會失去某些特性。 - 對于有
info.plist的bundle來說,CFBundleDisplayName代表其在操作系統(tǒng)中的顯示名,CFBundleName才是其真正意義上(代碼所用)的名稱。
Bundle和Package
A package is any directory that the Finder presents to the user as if it were a single file.
- 任意目錄,如果Finder將其作為單獨文件呈現(xiàn)給用戶,那么這個目錄就是一個package。
- 用戶可以通過
右鍵--->Show Package Contents查看package的內容。 - package的作用在于防止用戶對于package作出修改,維護package的統(tǒng)一性。
- 有些bundle也是package,例如application,因為用戶可以通過
Show Package Contents查看其內容;而有些不是,例如framework,它的內容可以直接查看。 - iOS的
.ipa解壓縮后,就得到了一個bundle,也是一個package。
Bundle的類型
- application
- framework
- loadable bundle / 含有可以載入的可執(zhí)行文件,例如OS X的插件,iOS的widget
- document package / 文檔包
Bundle的創(chuàng)建
通過xocde模版創(chuàng)建,applicaton / framework / loadable bundle都有自定特定的目錄結構。
-
通過shell腳本或手動創(chuàng)建目錄結構。
# 例子:通過腳本創(chuàng)建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 /
可執(zhí)行文件
Info.plist
app icon(如有)
app啟動圖片(如有)
storyboard / nib(如有)
Settins.bundle(用于在系統(tǒng)設置中顯示相關內容,如有)
通用資源文件(同本地化資源相對,如音頻,視頻,數據庫文件)
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能夠同時支持庫的多個版本。
- 靜態(tài)庫/動態(tài)庫可以單獨使用,也可以打包作為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可能包含可執(zhí)行文件,也可能不包含。(雖然文檔明確表示iOS不支持loadable bundle,但資源包類型的bundle也可以被使用)
MyLoadableBundle.bundle
Contents/
Info.plist
MacOS/
MyLoadableBundle // 可執(zhí)行文件,資源包沒有這個目錄
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是當前運行的可執(zhí)行文件及其資源所在的bundle。-
NSBundle的創(chuàng)建方式:
- [NSBundle mainBundle]。
- bundleWithPath: 根據絕對路徑創(chuàng)建。
- bundleWithIdentifier: 通過
CFBundleIdentifier找到已經載入內存的bundle。 - allFrameworks / allBundles: 返回相關的所有bundle。
- bundleForClass: 某個類(的二進制文件)所在的bundle。
資源搜索模式
系統(tǒng)在bundle中搜索資源時,分為兩步:
-
確定使用哪個本地化文件夾內的資源:
- 根據系統(tǒng)的
首選語言順序尋找相應地本地化文件夾?.lproj; - 如果沒找到,則根據
CFBundleDevelopmentRegion來確定選定本地化文件夾。
- 根據系統(tǒng)的
-
然后按照以下優(yōu)先級搜索:
- 通用資源目錄
- 特定地區(qū)資源目錄
- 特定語言資源目錄
- 開發(fā)語言資源目錄
- 注意,不會搜索上述目錄下的二級目錄,詳見
pathForResource:ofType:的文檔。 - 如果通用目錄和特定語言目錄有
同名資源,則后者永遠不會被找到。
根據iOS設備類型區(qū)分資源
-
資源命名方式
<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,即創(chuàng)建bundle對象。
- 從bundle中獲取資源路徑。注意,如果資源位于次級目錄,則必須指明路徑。
- 通過路徑創(chuàng)建對象。
例如,如下代碼從app bundle根目錄下的另一個bundle中獲取一張圖片。
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 在main bundle中找到特定bundle
NSString *sampleBundlePath = [[NSBundle mainBundle] pathForResource:@"SampleBundle.bundle" ofType:nil];
// 2. 載入bundle,即創(chuàng)建bundle對象
NSBundle *sampleBundle = [NSBundle bundleWithPath:sampleBundlePath];
// 3. 從bundle中獲取資源路徑
// 注意這里的圖片位于通用資源目錄下的Images二級目錄,相對路徑要明確這種關系
NSString *pic1Path = [sampleBundle pathForResource:@"pic1.png" ofType:nil];
// 4. 通過路徑創(chuàng)建對象
UIImage *image = [UIImage imageWithContentsOfFile:pic1Path];
}

通用資源目錄
-
所謂通用資源目錄,起初我以為是由Info.plist中的
CFBundlePackageType決定的,可能的類型包括:- APPL--->iOS應用
- FMWK--->framework
- BNDL--->lodable bundle
但試著刪除Info.plist后,系統(tǒng)依然遵循搜索模式,而非從bundle根目錄開始搜索。
接著,我試著打亂bundle下的目錄順序,例如將Contents和Resources目錄刪除,將所有資源文件任意放置,發(fā)現(xiàn)不論如何嘗試,都無法使用
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]; // 根據路徑創(chuàng)建圖片 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