最新
以下內(nèi)容可忽略。
參考
寫在前面
文章略長,可以先看下最后面的總結(jié)!
一般我們從bundle中獲取一張圖片,可以有這樣的獲取思路:
- 1)獲取主bundle
- 2)獲取自定義bundle
- 3)獲取自定義bundle中的資源
通常可以這樣寫:
//主bundle,也就是可執(zhí)行的工程的bundle
NSBundle *mainBundle = [NSBundle mainBundle];
//NSBundle *mainBundle = [NSBundle bundleForClass:[self class]];
//放在主工程中的自定義bundle
NSString *myBundlePath = [mainBundle pathForResource:@"MyBundle" ofType:@"bundle"];
NSBundle *myBundle = [NSBundle bundleWithPath:myBundlePath];
//放在自定義bundle中的圖片
NSString *imagePath = [myBundle pathForResource:@"123" ofType:@"png"];
self.image = [UIImage imageWithContentsOfFile:imagePath];
關(guān)于NSBundle
對于bundle
可以理解為一個捆綁包,個人理解bundle為一個獨立的空間,而我們的可執(zhí)行(executable
)工程,打包完之后,也是一個捆綁包,我們稱之為主bundle
,這個主bundle
包含了可執(zhí)行代碼,如各個viewcontroller的可執(zhí)行代碼,和相關(guān)資源例如圖片資源等。
NSBundle
這個類其實就是用來定位可執(zhí)行資源的。獲取到具體的可執(zhí)行文件的位置,然后再加載。因此,NSBundle
的使用,只限制于擁有獨立的bundle空間的(為什么不是:只限制于executable
的工程呢?因為對于動態(tài)庫,也可以看成是擁有獨立的bundle的對象。后面仔細分析)。
從NSBundle的文檔中可以看到這么一句:
Any executable can use a bundle object to locate resources, either inside an app’s bundle or in a known bundle located elsewhere. You don't use a bundle object to locate files in a container directory or in other parts of the file system.
大概翻譯一下的意思就是:
任何可執(zhí)行文件可以用來使用
NSBundle
對象來定位資源。無論是在應(yīng)用程序的包中,還是其他地方的已知包中。您不使用NSBundle
對象來在容器目錄或文件系統(tǒng)的其他部分中定位文件。
要求可執(zhí)行,我理解為運行時的可執(zhí)行,executable
是運行時,Dynamic Library
也是運行時加載,因此這兩個應(yīng)該符合上述的可用bundle
定位文件位置的要求
以上這段話如何理解呢?
在我們的APP工程中-->TARGETS -->Build Settings --> Linking -->Mach-Type有如下類型:
Executable
類型,也就是我們的可執(zhí)行類型,這樣的類型,通常都是需要有一個main
入口的。也就是我們常規(guī)的運行在手機上的每一個APP。在工程中我們找到main.m
文件:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
也就是我們的APP運行入口了。
除了Executable
類型,以下四種做區(qū)別對待:
動態(tài):
Dynamic Library
靜態(tài):
Bundle
Static Library
Relocatable Object File
靜態(tài)和動態(tài)的區(qū)別,就在于是否是運行時加載,靜態(tài)的在編譯時已經(jīng)決定了,編譯時將靜態(tài)的文件編譯進可執(zhí)行的工程;而動態(tài)的,只有在運行時,可執(zhí)行工程才會去加載。
關(guān)于bundled的加載,主要是對于mainBundle
和bundleForClass
的區(qū)別分析,下面我們再做具體分析:
mainBundle和bundleForClass
mainBundle
和bundleForClass
都是返回一個NSBundle
對象。
mainBundle
- 對于所有Mach-O Type類型,也就是上面提到的五種類型,
mainBundle
返回的都是可執(zhí)行工程的bundle
。
例如:有一個
Executable
工程Demo
,使用到了動態(tài)庫工程DynamicFramework
和靜態(tài)庫的工程StaticFramework
,那么無論是在Demo
中,還是DynamicFramework
和StaticFramework
中,最終mainBundle
返回的都是Demo
的bundle!
bundleForClass
Return Value
The NSBundle object that dynamically loaded aClass (a loadable bundle), the NSBundle object for the framework in which aClass is defined, or the main bundle object if aClass was not dynamically loaded or is not defined in a framework.
This method creates and returns a new NSBundle object if there is no existing bundle associated with aClass. Otherwise, the existing instance is returned.
大致的意思就是說,可以通過bundleForClass
獲取class所在的bundle,
特別是其中的這一句:
or the main bundle object if aClass was not dynamically loaded or is not defined in a framework.如果class是非動態(tài)的或者它不是定義在動態(tài)庫中,那么返回的是main bundle。
可以這樣理解:如果是對于Executable
類型的工程,或者是靜態(tài)的工程,無論class是屬于可執(zhí)行Executable
類型的工程,還是屬于其他的靜態(tài)庫,最終返回的是main bundle,相當于我們上面的[NSBundle mainBundle]
的返回結(jié)果。相反的,對于動態(tài)的工程,可以獲取到該工程的bundle
。
個人理解:動態(tài)的可以自成bundle(有屬于自己的空間)。因為靜態(tài)的在編譯期間,就已經(jīng)被打入主工程,主工程也就是(Executable
)工程。因此,bundleForClass
可以獲取到動態(tài)庫的bundle,而對于靜態(tài)庫,bundleForClass
獲取的是使用該靜態(tài)庫的主工程的bundle!
對于靜態(tài)庫
我們有這個一個可執(zhí)行工程(主工程)WxxDynamicDepotDemo
,一個靜態(tài)庫工程WxxStaticLibFramework
,靜態(tài)庫工程中有這個MyBundle.bundle
。MyBundle.bundle
中就一張圖片:
仿照MJRefresh
中NSBundle+MJRefresh
的寫法,寫了一個用于獲取bundle
和image
的分類:
#import <UIKit/UIKit.h>
@interface NSBundle (mybundle)
+(instancetype)my_bundle;
+(UIImage *)my_image;
@end
#import "NSBundle+mybundle.h"
#import "FrameworkBundleManager.h"
@implementation NSBundle (mybundle)
+(instancetype)my_bundle{
static NSBundle *myBundle = nil;
if (myBundle == nil) {
NSBundle *mainBundle = [NSBundle bundleForClass:[FrameworkBundleManager class]];
NSString *myBundlePath = [mainBundle pathForResource:@"MyBundle" ofType:@"bundle"];
myBundle = [NSBundle bundleWithPath:myBundlePath];
}
return myBundle;
}
+(UIImage *)my_image{
static UIImage *myImage = nil;
if (myImage == nil) {
NSString *path = [[self my_bundle]pathForResource:@"123" ofType:@"png"];
myImage = [UIImage imageWithContentsOfFile:path];
}
return myImage;
}
@end
FrameworkBundleManager
是靜態(tài)庫內(nèi)部的文件:
NSBundle *mainBundle = [NSBundle bundleForClass:[FrameworkBundleManager class]];
在靜態(tài)庫的工程WxxStaticLibFramework
內(nèi)部,寫了這樣一句:
NSLog(@"static framework內(nèi)部獲取:%@",[NSBundle my_bundle].bundlePath);
最終的結(jié)果是:
static framework內(nèi)部獲取:/Users/hncy-ios/Library/Developer/CoreSimulator/Devices/F4962723-AF32-44D2-A5DC-142DFDA30B4D/data/Containers/Bundle/Application/E6D53415-ED10-458F-997C-E49FAA590B7C/WxxDynamicDepotDemo.app/MyBundle.bundle
可以看到WxxDynamicDepotDemo.app
,那么這個就充分說明了以上的觀點。同樣的,從主工程獲取靜態(tài)庫中的一個bundle
。其實是獲取不到的,因為,編譯的后,靜態(tài)庫中的class都歸屬于主工程,而通過bundleForClass
去獲取,只能獲取主工程的bundle
。
所以導(dǎo)致的結(jié)果是:
靜態(tài)庫中放了一個
bundle
,可是靜態(tài)庫中通過bundleForClass
或者mainBundle
去獲取,卻是主工程(可執(zhí)行工程)中的bundle,訪問不到靜態(tài)庫內(nèi)部的bundle
(或許說,靜態(tài)庫就沒有bundle
)。
對于靜態(tài)庫的bundle獲取大致的理解如圖所示:
對于動態(tài)庫
同樣的,在動態(tài)庫工程中,同樣加入自定義bundle
:MyBundle
無論是對于靜態(tài)庫還是動態(tài)庫,將工程拖入主工程,編譯的時候即可關(guān)聯(lián)編譯:
對于動態(tài)庫的加載,這里提供一個思路,之后另起一篇(鏈接暫時無效)。
- 獲取動態(tài)庫的路徑path,有可能是工程的bundle中(這個需要解壓ipa包,加入動態(tài)庫,并且重簽名),也有可能從沙盒加載(從網(wǎng)絡(luò)下載,存進進沙盒)。
- 判斷動態(tài)庫是否存在,如果存在,根據(jù)動態(tài)庫名字加載。
- 如果加載動態(tài)庫成功,使用performSelector調(diào)用動態(tài)庫中的方法。
在動態(tài)庫內(nèi)部,寫了獲取圖片資源的方法如下:
-(UIImage*)dynamic_image{
NSBundle *b = [NSBundle bundleForClass:[self class]];
// NSBundle *b = [NSBundle mainBundle];
NSLog(@"動態(tài)庫獲取bundle路徑:%@",b.bundlePath);
NSString *path = [b pathForResource:@"MyBundle" ofType:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:path];
NSString *imagePath = [bundle pathForResource:@"123" ofType:@"png"];
return [UIImage imageWithContentsOfFile:imagePath];
}
結(jié)果打印了:
動態(tài)庫獲取bundle路徑:/Users/hncy-ios/Library/Developer/Xcode/DerivedData/WxxDynamicDepotDemo-dfhfmsnghwoiifgyyovgmpbnfoan/Build/Products/Debug-iphonesimulator/WxxDynamicDepotFramework.framework
可以看到,路徑是WxxDynamicDepotFramework.framework
,而不是WxxDynamicDepotDemo.app
。
因此,對于動態(tài)庫的理解,可以大致如下圖:
總結(jié)
- 可執(zhí)行工程,動態(tài)庫工程,都可以獲取到獨立的
bundle
,靜態(tài)庫不行。 -
mainBundle
無論寫在哪里,都是獲取主工程的main bundle
。而bundleForClass得區(qū)別對待,如果傳入的是庫中的class
,靜態(tài)庫中獲取的是主工程的bundle
,動態(tài)庫中獲取的是動態(tài)庫的bundle
。
2019年01月27日更新
評論有人問:
怎么才能獲取到靜態(tài)庫的resource.bundle文件。
問這個問題有兩個原因:
- 1,沒搞懂靜態(tài)庫,動態(tài)庫和以及使用靜態(tài)庫/動態(tài)庫的主工程之間的意義。
- 2、我的解釋不夠明白(畢竟是當時剛接觸就寫了這篇理解,打算另起一篇。)
一般一個項目工程,導(dǎo)入靜態(tài)庫,需要導(dǎo)入靜態(tài)庫的包和屬于靜態(tài)庫的資源文件。也就是說,靜態(tài)庫中使用的資源,是放在使用它的工程中去的,也就是framework中所使用的資源bundle,除了靜態(tài)庫中的代碼是打包在framework中,其他文件都是放在外部的bundle文件中的。
以上圖項目為例,SDK001.framework
如何使用SDK001.bundle
中的圖片資源:
SDK001.framework中的代碼:
NSString *bundlePath = [[NSBundle mainBundle]pathForResource:@"SDK001" ofType:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
NSString *file = [bundle pathForResource:"imageName" ofType:@"imageFormat"];
UIImage *image = [UIImage imageWithContentsOfFile:file2];
是不是so easy?
如果您覺得本文對您有一定的幫助,請隨手點個喜歡,十分感謝!