iOS:NSBundle的一些理解

最新

iOS SDK(二):Bundle

以下内容可忽略。

参考

写在前面

文章略长,可以先看下最后面的总结!

一般我们从bundle中获取一张图片,可以有这样的获取思路:

  • 1)获取主bundle
  • 2)获取自定义bundle
  • 3)获取自定义bundle中的资源

通常可以这样写:

//主bundle,也就是可执行的工程的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];

关于NSBundle

对于bundle可以理解为一个捆绑包,个人理解bundle为一个独立的空间,而我们的可执行(executable)工程,打包完之后,也是一个捆绑包,我们称之为主bundle,这个主bundle包含了可执行代码,如各个viewcontroller的可执行代码,和相关资源例如图片资源等。

NSBundle这个类其实就是用来定位可执行资源的。获取到具体的可执行文件的位置,然后再加载。因此,NSBundle的使用,只限制于拥有独立的bundle空间的(为什么不是:只限制于executable的工程呢?因为对于动态库,也可以看成是拥有独立的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.

大概翻译一下的意思就是:

任何可执行文件可以用来使用NSBundle对象来定位资源。无论是在应用程序的包中,还是其他地方的已知包中。您不使用NSBundle对象来在容器目录或文件系统的其他部分中定位文件。

要求可执行,我理解为运行时的可执行,executable是运行时,Dynamic Library也是运行时加载,因此这两个应该符合上述的可用bundle定位文件位置的要求

以上这段话如何理解呢?
在我们的APP工程中-->TARGETS -->Build Settings --> Linking -->Mach-Type有如下类型:


项目类型.png

Executable类型,也就是我们的可执行类型,这样的类型,通常都是需要有一个main入口的。也就是我们常规的运行在手机上的每一个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类型,以下四种做区别对待:
动态:

  • Dynamic Library

静态:

  • Bundle
  • Static Library
  • Relocatable Object File

静态和动态的区别,就在于是否是运行时加载,静态的在编译时已经决定了,编译时将静态的文件编译进可执行的工程;而动态的,只有在运行时,可执行工程才会去加载。

关于bundled的加载,主要是对于mainBundlebundleForClass的区别分析,下面我们再做具体分析:

mainBundle和bundleForClass

mainBundlebundleForClass都是返回一个NSBundle对象。

mainBundle

  • 对于所有Mach-O Type类型,也就是上面提到的五种类型,mainBundle返回的都是可执行工程的bundle

例如:有一个Executable工程Demo,使用到了动态库工程DynamicFramework和静态库的工程StaticFramework,那么无论是在Demo中,还是DynamicFrameworkStaticFramework中,最终mainBundle返回的都是Demo的bundle!

bundleForClass

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是非动态的或者它不是定义在动态库中,那么返回的是main bundle。

可以这样理解:如果是对于Executable类型的工程,或者是静态的工程,无论class是属于可执行Executable类型的工程,还是属于其他的静态库,最终返回的是main bundle,相当于我们上面的[NSBundle mainBundle]的返回结果。相反的,对于动态的工程,可以获取到该工程的bundle

个人理解:动态的可以自成bundle(有属于自己的空间)。因为静态的在编译期间,就已经被打入主工程,主工程也就是(Executable)工程。因此,bundleForClass可以获取到动态库的bundle,而对于静态库,bundleForClass获取的是使用该静态库的主工程的bundle!

对于静态库

我们有这个一个可执行工程(主工程)WxxDynamicDepotDemo,一个静态库工程WxxStaticLibFramework,静态库工程中有这个MyBundle.bundleMyBundle.bundle中就一张图片:

文件结构示意图.png
图片资源.png

仿照MJRefreshNSBundle+MJRefresh的写法,写了一个用于获取bundleimage的分类:

#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是静态库内部的文件:

NSBundle *mainBundle = [NSBundle bundleForClass:[FrameworkBundleManager class]];

在静态库的工程WxxStaticLibFramework内部,写了这样一句:

NSLog(@"static framework内部获取:%@",[NSBundle my_bundle].bundlePath);

最终的结果是:

static framework内部获取:/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,那么这个就充分说明了以上的观点。同样的,从主工程获取静态库中的一个bundle。其实是获取不到的,因为,编译的后,静态库中的class都归属于主工程,而通过bundleForClass去获取,只能获取主工程的bundle

所以导致的结果是:

静态库中放了一个bundle,可是静态库中通过bundleForClass或者mainBundle去获取,却是主工程(可执行工程)中的bundle,访问不到静态库内部的bundle(或许说,静态库就没有bundle)。

对于静态库的bundle获取大致的理解如图所示:

静态库示意图.png

对于动态库

同样的,在动态库工程中,同样加入自定义bundleMyBundle

动态工程文件示意.png

无论是对于静态库还是动态库,将工程拖入主工程,编译的时候即可关联编译:


关联工程.png

对于动态库的加载,这里提供一个思路,之后另起一篇(链接暂时无效)。

  • 获取动态库的路径path,有可能是工程的bundle中(这个需要解压ipa包,加入动态库,并且重签名),也有可能从沙盒加载(从网络下载,存进进沙盒)。
  • 判断动态库是否存在,如果存在,根据动态库名字加载。
  • 如果加载动态库成功,使用performSelector调用动态库中的方法。

在动态库内部,写了获取图片资源的方法如下:

-(UIImage*)dynamic_image{
    NSBundle *b = [NSBundle bundleForClass:[self class]];
//    NSBundle *b = [NSBundle mainBundle];
    NSLog(@"动态库获取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];
}

结果打印了:

动态库获取bundle路径:/Users/hncy-ios/Library/Developer/Xcode/DerivedData/WxxDynamicDepotDemo-dfhfmsnghwoiifgyyovgmpbnfoan/Build/Products/Debug-iphonesimulator/WxxDynamicDepotFramework.framework

可以看到,路径是WxxDynamicDepotFramework.framework,而不是WxxDynamicDepotDemo.app

因此,对于动态库的理解,可以大致如下图:

动态库示意图.png

总结

  • 可执行工程,动态库工程,都可以获取到独立的bundle,静态库不行。
  • mainBundle无论写在哪里,都是获取主工程的main bundle。而bundleForClass得区别对待,如果传入的是库中的class,静态库中获取的是主工程的bundle,动态库中获取的是动态库的bundle

2019年01月27日更新

评论有人问:

怎么才能获取到静态库的resource.bundle文件。

问这个问题有两个原因:

  • 1,没搞懂静态库,动态库和以及使用静态库/动态库的主工程之间的意义。
  • 2、我的解释不够明白(毕竟是当时刚接触就写了这篇理解,打算另起一篇。)

一般一个项目工程,导入静态库,需要导入静态库的包和属于静态库的资源文件。也就是说,静态库中使用的资源,是放在使用它的工程中去的,也就是framework中所使用的资源bundle,除了静态库中的代码是打包在framework中,其他文件都是放在外部的bundle文件中的。

静态库包含到工程中了.png

以上图项目为例,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?

如果您觉得本文对您有一定的帮助,请随手点个喜欢,十分感谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容