主要解决以下三个问题:
- 自己封装的framework库有多个公共的方法,model,类等,所以打算封装一个公共类库,让其他的framework库进行依赖;
- framework库中包含图片资源,plist文件的读取 ;
- 资源文件bundle的创建方式;
1. 一般公共类库的封装
不涉及资源问题,就是一些公共方法的封装,封装流程和一般的framework库没区别,注意点:
- 暴露.h文件;
- 设置成release版本;
- 如果用了Category分类,需要在工程中
other linker flags
中添加-objC
,如果依然有问题,再添加-all_load
; - 关于符号表的警告,这时需要将
Generate Debug Symbols
设置为NO
即可关闭符号表警告; - 如果需要支持
bitcode
:在TAGETS
的Build setting
中搜索Other C Flags
,添加命令-fembed-bitcode
。同样的设置在PROJECT
中。
2. 创建资源文件bundle
两种方式
- 直接创建文件夹,拖入文件,再修改后缀为.bundle;
此方法缺点是如果有新的资源需要导入,又得重新在创建文件夹,把上一次的资源全部拷贝进来,更新新的资源,很麻烦;- 使用Xcode生成,具体如下:
-
创建Bundle文件:创建工程,选择如下:
-
进行配置:
info.plist文件处理:(打包的bundle给别人使用,别人在打包完上传过程中可能会极大的坑,加上如下处理)
然后把图片导入工程,build即可;分别选择真机和模拟器,然后各运行一遍,即可生成真机和模拟器使用的bundle;
3. 创建包含资源和依赖库的framework库
- 创建的SDK,这里命名为 AM300QuestionSDK,选择的是framework库;
- 把上面创建的依赖库 AM300PublicSDK.framework添加到AM300QuestionSDK文件夹下;
- 对SDK进行配置:具体配置和上面创建framework库一样;
- 把工程中使用到公共类库的地方,修改成: #import <AM300PublicSDK/XXX.h>;
- 把创建好的资源: questionimages.bundle 导入到AM300QuestionSDK下;
- 资源文件的调用问题修改:
因为现在iOS只能创建自己的静态库,静态库资源加载方式:
plist文件如下:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Am300QuestionSDK.framework/questionimages" ofType:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
NSString *plistPath = [bundle pathForResource:@"QuestionnaireList" ofType:@"plist"];
image如下:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Am300QuestionSDK.framework/questionimages" ofType:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
NSString *fileName = [NSString stringWithFormat:@"%@%@",imageName,@"@2x.png"];
NSString *filePath = [bundle pathForResource:fileName ofType:nil];
UIImage *img = [UIImage imageWithContentsOfFile:filePath];
修改完,然后运行生成framework文件;
4. 库的使用
-
创建一个工程,导入SDK及其依赖库,创建SDK的调用类CRQuestionViewController,用于验证SDK是否正常工作;
-
现在运行工程,发现可以正常运行,但是资源文件没有加载成功,
因为静态库framework读取资源文件需要额外创建一个copy Files 进行读取
操作如下:
然后运行,成功.
注:不管是资源文件.bundle还是framework库,都存在真机和模拟器,上架需要记得使用真机生成;
上面第二步一定不能少,除非你的资源文件是直接放在主工程目录下的;不过不把资源文件放在framework库中,那也就没必要打framework库了,直接用.a库也行了,主要还是看需求;
参考链接:
https://www.jianshu.com/p/c92c08d8afda
https://blog.csdn.net/ljl5125/article/details/52210283
https://www.jianshu.com/p/fb5083f2c0d2
5.创建可调式工程
以上的方式,如果SDK有问题,修改很麻烦,所以可以创建可调试工程,运行没问题直接生成framework库,操作如下:
创建项目工程TiaoShiDemo
通过TARGETS 添加 一个我们要创建的SDK,这里命名为 AM300QuestionSDK,选择的是framework库,不创建.a
把我们SDK需要的类导入AM300QuestionSDK文件夹下;
把上面创建的依赖库 AM300PublicSDK.framework添加到AM300QuestionSDK文件夹下;
通过TARGETS—> AM300QuestionSDK :对SDK进行配置:具体配置和上面创建framework库一样;
把工程中使用到公共类库的地方,修改成: #import <AM300PublicSDK/XXX.h>
选择AM300QuestionSDK,进行编译,编译通过后,选择TiaoShiDemo编译,发现报错;因为我们的TiaoShiDemo工程没有AM300PublicSDK.framework,需要添加一个;
把创建好的资源: questionimages.bundle 导入到AM300QuestionSDK下;
然后运行工程即可
问题:
这样创建的AM300QuestionSDK,在访问资源时,需要使用如下方式:
/得到framework路径
#define FrameworkPath [[NSBundle mainBundle] pathForResource:@"Frameworks/AM300QuestionSDK" ofType:@"framework"]
//得到对应的bundle路径
#define FrameworkBundle [NSBundle bundleWithPath:FrameworkPath]
//得到资源的bundle路径
#define VivienBundle [NSBundle bundleWithPath:[FrameworkBundle pathForResource:@"questionimages" ofType:@"bundle"]]
//获取资源路径,文件实例:
NSString *plistPath = [VivienBundle pathForResource:@"QuestionnaireList" ofType:@"plist"];
但是如果用上面的访问方式,最后生成framework,在使用时,资源读取会失败;导致我在生成framework库前,又要更改资源的读取方式,不知道是哪里有问题没有设置好,有知道的仁兄指点一二.
一些基本设置说明:
-ObjC
我们知道在Objective-C中方法调用都是在运行期确定的,所以Objective-C没有针对每个方法定义链接符号,它只每个类创建链接符号。因此当在一个静态库中使用类别来扩展已有类的时候,链接器不知道如何把类原有的方法和类别中的方法整合起来,就会导致你调用类别中的方法时出现selector not recognized的错误。
设置ObjC标志后,链接器会把一个类相关的所有目标文件都加载进来,这样就解决了这个问题。由于这样做会使可执行文件体积变大,所以需要需要自己手动设置一下。
官方的说明链接:https://developer.apple.com/library/archive/qa/qa1490/_index.html
参考链接:https://www.jianshu.com/p/fb5083f2c0d2
-all_load 和 -force_load
在64位ios应用环境下,在静态库中只有category而没有对应的class定义时-ObjC标志会失效(这是链接器的一个bug)
这时可以使用-all_load强制加载所有目标文件 或者使用-force_load指定加载某一个包
千万不要随便使用这个参数!假如你使用了不止一个静态库文件,然后又使用了这个参数,那么你很有可能会遇到ld: duplicate symbol错误,因为不同的库文件里面可能会有相同的目标文件
所以建议在遇到-ObjC失效的情况下使用-force_load参数
-force_load所做的事情跟-all_load其实是一样的
但是-force_load需要指定要进行全部加载的库文件的路径
这样的话,你就只是完全加载了一个库文件,不影响其余库文件的按需加载
2021.01.13新增:模拟器包含arm64打包合并问题
只需要对模拟器的.a进行处理即可,终端输入如下
lipo 路径/XXX.a -remove arm64 -output 路径/XXX.a