iOS SDK开发系列:
iOS SDK(一):静态库、动态库创建&接口测试
iOS SDK(二):Bundle
...
待更新..
一、回顾
回顾上一篇,你可以理解并认识到Bundle
在 SDK
中的作用:用来存放资源文件。SDKDemo
是使用SDK
的工程,由图可见,.a / .framework 、
和 .bundle
需要导入工程才能使用。
本篇来介绍下,什么是Bundle
,如何读取Bundle
中的资源。
二、什么是bundle
独立的物理空间,存放各种资源。
App开发与SDK开发
SDK开发中,我们把工程分为 SDK 工程
和 Demo 工程
如果是常规的App
开发,我们只会接触到app 工程
,也就是本文中的Demo 工程
,也就是有启动入口的能够独立运行的二进制程序。对于Mach-O Type
中的Executable
类型。
而 SDK工程
我们开发编译出 .a / .framework
,的二进制库。而 .bundle
,包含了各种资源文件(图片、编译过的二进制文件、故事板文件、SSL证书等),使用时需要导入给app 工程
, SDK工程
内部需要实现读取这个bundle
中的资源并使用其中的资源。
理清了App
开发与SDK
开发的区别,下面介绍不同开发读取资源的不同方式。
App开发中
在app 工程
中,我们将图片放置在工程目录中,可以通过如下方式获取到图片资源:
NSBundle *bundle = [NSBundle mainBundle];
// 或者:
// NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *imagePath = [bundle pathForResource:@"imageName.png" ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
imagePath
的路径是这样的:...Demo.app/imageName.png
可以看出 无论是[NSBundle mainBundle]
还是[NSBundle bundleForClass:[self class]]
指向的bundle
都是Demo.app
。我们可以把mainBundle
理解为 **.app
的内部。
SDK工程中
对于 SDK 工程
读取 SDK.bundle
的资源内容,相当于...Demo.app/imageName.png
中间多嵌套一层:
...Demo.app/SDK.bundle/imageName.png
也就是说,我们读取bundle
中的资源,其路径是:mainBundle
的路径 + 自定义bundle
名称 + 文件名。
如下:
NSBundle *mainBundle = [NSBundle mainBundle];
NSString *sdkBundlePath = [mainBundle pathForResource:@"SDK" ofType:@"bundle"];
NSBundle *sdkBundle = [NSBundle bundleWithPath: sdkBundlePath];
NSString *filePath = [sdkBundle pathForResource:imageName ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
那么在 SDK工程
中,获取mainBundle
,能使用这个吗NSBundle * mainBundle = [NSBundle bundleForClass:[self class]];
?毕竟 self
是属于 SDK工程
中的类,这样用的话不就是跨领域了吗?
事实证明是可以的,可以用下图理解。
红色实线框,代表命名空间(独立空间)。
虚线框,代表归属于外部实线框。
对于静态库来说:
静态库在app中的关系.png在iOS工程中,工程内部class
是没有命名空间的,同一个工程,不能存在两个同样名字的class
,就算用文件夹隔开也不行。因此,可以理解为无论是app
工程的class
还是SDK
工程的class
编译链接到同一个命名空间(.app/mainBundle
),因此SDK
工程中bundleForClass
最终获取到的依旧是mainBundle
。这也就是为什么,静态库开发中,静态库工程的class
、全局变量都需要加上特殊前缀(或者重命名),以避免跟调用SDK
的工程产生重复,导致编译错误。
而我们使用的bundle
,是独立的命名空间。上图bundle
嵌套于mainBundle
中。因此读取bundle
中的资源,也就需要通过
`mainBundle`的路径 + 自定义`bundle`名称 + 文件名
来读取了。
以上分析,是对于静态库而言。
动态库则是class
和bundle
都各自归属于独立的命名空间,因此通过bundleForClass
来获取mainBundle
就不适用了。
通过创建一个动态库工程来测试,不清楚如何创建,可参考iOS SDK(一):静态库、动态库创建&接口测试
在动态库的SDK
工程中,我们调用bundleForClass
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSLog(@"%@",bundle.bundlePath);
获取到的bundle
地址的后段是:
.../Demo.app/Frameworks/SDK.framework
这是动态库跟静态库的区别了,动态库有自己独立的空间。我们通过解压集成动态库的ipa
包,也可以直观的发现动态库***.framework
就处于.../***.app/Frameworks/
的路径下。Frameworks
是用于存放动态库的文件夹。而对于静态库,解压ipa
之后,是找不到的具体的.framework
文件的。
而我们独立于***.framework
的bundle
,如果按照静态库的导入方式,则是存在于.../***.app/.bundle
路径之下。
既然静态库和动态库,在使用bundleForClass
存在如此的差异,使用的时候注意区分。
其实还有一种方式:
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *sdkBundlePath = [bundle pathForResource:@"SDK" ofType:@"bundle"];
NSBundle *sdkBundle = [NSBundle bundleWithPath:sdkBundlePath];
NSString *filePath = [sdkBundle pathForResource:@"test" ofType:@"png"];
就是无论静态库还是动态库,我们把自定义的bundle
放在***.framework
中,这样bundleForClass
就通用了,工程导入SDK的时候,也不会漏掉bundle
文件。
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *sdkBundlePath = [bundle pathForResource:@"SDK" ofType:@"bundle"];
NSBundle *sdkBundle = [NSBundle bundleWithPath:sdkBundlePath];
NSString *filePath = [sdkBundle pathForResource:@"test" ofType:@"png"];
这样得到
- 动态库的资源路径是:
.../Demo.app/Frameworks/SDK.framework/SDK.bundle/test.png
- 静态库的资源路径是:
.../Demo.app/SDK.bundle/test.png
完美。
总结
-
静态库
class
在宿主工程中没有独立的命名空间,编译后跟宿主工程是链接到一个位置的,假如SDK
中使用了第三方库,则需要添加特殊的前缀(或者重命名),防止宿主工程使用了同样的第三方库导致编译出错。动态库则无需注意这个问题,可以放心大胆直接用第三方库。 -
动态库实测过可以上架的,只是
iOS10之后真机
不能再从沙盒加载动态库了(模拟器
还是可以的),也就不存在可以热更的可能。 -
SDK
工程内部如何读取bundle
,取决于bundle
的位置。
1、对于静态库是固定的位置:.../***.app/***.bundle/
,先获取.app
路径.../***.app/
,mainBundle
和bundleForClass
效果一致,再根据bundle
名字获取路径下的***.bundle
;
2、对于动态库,如果使用mainBundle
,则是.../***.app/
;
如果使用bundleForClass
,是.../***.app/Frameworks/***.framework/
。再根据bundle
名字获取路径下的***.bundle
。
网友评论