静态库和动态库的制作以及Bundle资源文件的使用

前两天因为公司业务上的需要,我们需要和联通公司那边进行业务集成,考虑到一些业务上的隐私性,我们将提供的内容打包成了一个静态库,只为对方提供了一些接口,今天正好有空整理一下,填一下这个过程中遇到的一些坑,以方便日后查阅。

什么是库?

库是程序代码的集合,是共享程序代码的一种方式。我们根据源代码的公开情况,可以将库分为 2 种类型

开源库:公开源代码,我们能看到代码的具体实现,比如SDWebImage、AFNetworking等;如何将自己的代码发布到代码托管平台中,然后借助CocoaPods供别人使用,我们在上一篇文章中已经讲过,这里不再细说。

闭源库:不公开源代码,是经过编译后的二进制文件,看不到代码的具体实现。闭源库主要分为:静态库、动态库

静态库和动态库的存在形式以及使用上的区别

存在形式:

静态库:.a 和 .framework

动态库:.dylib 和 .framework

区别:

静态库:链接时,静态库会被完整地复制到可执行文件中, 被多次使用就有多份冗余拷贝 ,如下图:

静态库

动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,以节省内存,具体形式如下图所示:

动态库

静态库的使用场景:

项目开发中我们经常使用到的极光推送、百度地图、友盟分享等等。这些大公司都有自己的核心业务,同时又希望我们去使用他们提供的技术,但是又不想暴露他们的代码,因此他们采用"闭源"的方式来让我们集成。

将MRC的项目放到ARC环境下,我们可以如下图去操作

MRC->ARC

但是如果文件特别多,我们这样一个个去操作是不是很麻烦?其实我们可以将MRC的项目,打包成静态库, 可以在ARC下直接使用, 不需要转换,非常方便;

知识准备:

我们都知道模拟器下的静态库和真机下的静态库是不能混用的,那为什么呢?其实主要原因是模拟器和真机的CPU架构不一样(各个模拟器型号之间架构也不一样)不同机型的CPU, 对应的架构不同;

模拟器:

4s和5使用的是i386

5s-6sPlus使用的是x86_64

真机:

3gs---4s :armv7

5/5c :armv7s(armv7兼容armv7s)

5s---6sPlus:arm64

我们可以分别选中不同的模拟器, 进行编译,然后利用lipo -info xxx.a查看静态库所支持的架构, 如下图所示:

查看静态库支持架构

因为CPU架构的不同,这就造成了一个问题,如果我是在6s环境下编译生成的静态库运行到5上面就会编译不通过, 怎样可以一次编译支持多个架构的的静态库呢?

可以通过Build Settings -> Build Active -> NO,表示不止编译活跃的架构, 让所有的架构都编译,如下图所示:

编译所有架构

.a静态库的制作步骤

新建项目-> 选择 “Cocoa Touch Static Library”

新建静态库

添加库需要包含的源代码,并且设置需要暴露的头文件:

3.png

引入制作的静态库所需要的网络框架(有些会自动引入,有些需要手动引入,根据编译报错,可以检查。)

引入框架

设置支持所有架构:

设置支持架构

编译前检查一下是debug模式还是release模式,选择release模式。在模拟器和真机中分别编译后,libzpstaticLibrary.a就由红色变成了黑色,Show in Finder在目录中查看:

Release模式

.framework静态库的制作步骤

新建项目-> 选择 “Cocoa Touch Framework”

Framework

添加库需要包含的源代码,并且设置需要暴露的头文件:

暴露头文件

如果用户需要导入的头文件过多怎么办?我们可以使用一个主头文件包含其他头文件, 让用户只导入一个主头文件。

编译时, 设置编译所有架构

设置编译架构

因为默认制作的是动态库,我们 需要设置链接类型target -> Build Settings-> 搜索 Mach-o Type ;改为静态库

设置类型

如果没有这一步,我们在测试的时候会报一个错误:

错误提示

这个错误是因为我们默认生成的是一个动态库,如果我们把它当成一个动态库来用的话 就需要在测试工程中General->Embedded Binaries中导入我们这个动态库,这样就不会报这个错误了。

动态库

经过以上步骤后我们就可以成功的生成了一个.Framework的静态库或者是动态库了。

静态库的操作

合并静态库

因为静态库针对于模拟器和真机生成了不同版本(支持不同架构), 所以没法同时运行,但是我们可以将二者进行合并,合并后的.a大小大约是不合并的2倍左右。

cd 到一个目录,然后通过

lipo -create Debug-iphoneos/libTools.a  Debug-iphonesimulator/libTools.a  -output  libTools.a

进行合并,合并后的架构如下图所示:

分解合并库

既然有合并静态库,那么对应的也有分解合并库,例如我们在发布的时候只想要使用arm64架构,那么我们可以通过下面这行命令来达到我们目的:

lipo -thin arm64 静态库 -output 新的静态库名称

如下图所示:

查看分解之后的库可以发现该库已经非常小了

从合成库中移除某个架构

如果合成库中的某一个架构我们用不到了,想要移除,那么我们可以通过下面的命令,例如我们想要将合成库中的i386移除

lipo -remove i386 静态库 -output 静态库名称

执行命令后我们可以发现,合成的静态库中已经没有了i386这个架构了

总结

.a静态库和.framework静态库的区别?

我们都知道静态库包括.a和.framework,那么二者之间到底有什么区别呢?

.a是一个纯二进制文件,  而.framework中除了有二进制文件之外还有资源文件,比如Bunle、Plist等。

.a文件不能直接使用, 至少要有.h文件的配合;.framework文件可以直接使用

.a + .h + sourceFile = .framework

建议使用.framework

静态库的调试

因为静态库只提供了一些头文件,实现代码都是经过二进制化的,如果我们想对静态库进行断点调试如何做呢?

我们可以创建复合项目来对静态库进行调试:

创建一个测试工程,选中TARGETS,点击下面的➕按钮,添加一个静态库,如下图所示

复合项目

设置主工程文件和静态库的依赖关系。

添加依赖关系

经过以上两步后我们就可以对要制作的静态库文件进行调试开发了,是不是很爽,至于制作静态库就和上面的步骤一样了。

Bundle封装XIB、图片等资源文件

创建Bundle文件

因为我是新建了一个复合测试工程,所以在这里为了方便我们直接在复合测试工程中添加了一个子工程,因为iOS框架中没有bundle,要选中macOS框架找到bundle,如下图为我们的Bundle命名为STWNetPay

image

Bundle的本质其实是一个目录,我们完全可以创建一个文件夹,然后把需要封装的资源文件拷贝到该目录下,然后将后缀名为改成bundle即可,但是如果我们想要将XIB文件封装到Bundle文件中的话,这种方法就不可以了. 考虑到xib是文本文件,编译后要被序列化为二进制的nib文件,使用时将nib文件反序列化,就可以正常显示界面了。而bundle本身是静态的,其内部的资源包不参与项目的编译,所以,此处必须创建工程把xib序列化为二进制的nib,否则的话,直接创建文件夹后改名就可以了。

设置Bundle

1.创建好之后我们发现里面只有一个Info.plist文件,经测试该文件不能删掉,如果删掉之后编译Bundle文件会报错:bundle format unrecognized, invalid, or unsuitable

2.iOS Deployment Target改为你支持的最低版本

image

3.设置base SDK 为latest iOS

image

4.COMBINE_HIDPI_IMAGES设置为NO

因为iOS创建Bundle时放入的图片资源(.png)在默认配置下会被转为.tiff格式,使用的时候找不到。所以找到bundle的工程,Buld Settings > COMBINE_HIDPI_IMAGES设置为NO

image

5.Skip Install 设置为NO

image

6."Build Active Architecture Only" 设置为 NO

向Bundle中添加资源文件

image

一切就绪,按快捷键"Command + B"编译,编译成功后,我们的Bundle文件即由红色变成了黑色,表示编译成功,这个时候找到我们的Bundle文件,显示报内容,如下图所示:

image

可以看到XIB文件经过编译已经变成了nib文件,这就是我们想要的.

使用bundle里的资源文件

将编译好之后的Bundle文件拖入我们的静态库中,因为是要在静态库中使用Bundle文件,而不是在MainBundle中,所以我们需要先找到我们Bundle所在的路径才能使用它里面的资源.

创建一个管理Bundle路径的类BundleTools,专门用来获取我们Bundle所在的路径:

#import<Foundation/Foundation.h>#define BUNDLE_NAME @"STWNetPay"@interfaceBundleTools:NSObject+ (NSString*)getBundlePath: (NSString*) assetName;+ (NSBundle*)getBundle;@end

#import"BundleTools.h"@implementationBundleTools+ (NSBundle*)getBundle{NSURL*url=  [[NSBundlebundleForClass:[selfclass]] URLForResource:BUNDLE_NAME withExtension:@"bundle"];NSBundle*bundle = [NSBundlebundleWithURL:url];returnbundle;}+ (NSString*)getBundlePath: (NSString*) assetName{NSBundle*myBundle = [BundleTools getBundle];if(myBundle && assetName) {return[[myBundle resourcePath] stringByAppendingPathComponent: assetName];    }returnnil;}@end

使用图片

UIImage*image = [UIImageimageWithContentsOfFile: [BundleTools getBundlePath:@"nav_back"]];

使用XIB

控制器是XIB的情况,此时应该在控制器中重写Init方法

- (instancetype)init{NSBundle*myBundle = [BundleTools getBundle];//从bundle中获取界面文件self= [superinitWithNibName: [NSStringstringWithUTF8String: object_getClassName(self)] bundle: myBundle];if(self) {// Custom initialization}returnself;}

否则

NSBundle*myBundle = [BundleTools getBundle];//从bundle中获取界面文件view = [myBundle loadNibNamed:@""owner:niloptions:nil].firstObject;

Swift打包动态库

因为Swift项目是不支持静态库的,必须使用动态库。所以我们在新建一个静态库的时候,一定要选择Cocoa Touch Framework

创建动态库

如果我们在项目中将target -> Build Settings-> 搜索 Mach-o Type ;改为Static Library

静态库

Command+B编译,系统会报一个错,提示我们Swift is not supported for static libraries.如下图:

静态库报错

所以,如果在Swift项目中,我们必须使用动态库;

因为Swift是没有头文件这个说法的,所以在Build Phases中我们也不用设置暴露头文件了

11.png

确定支持模拟器或者真机中的所有架构

编译架构

最重要的,因为swift没有头文件,所以如果我们想让别人用到我们的方法,就用Public修饰符修饰。

暴露方法

这样生成的动态库中就会将我们的Swift方法转换成了OC的方法

SWIFT_CLASS("_TtC10zpSwiftLib4Tool")@interfaceTool:NSObject+ (void)Log;- (nonnullinstancetype)init OBJC_DESIGNATED_INITIALIZER;@end

这样将生成的动态库拖入到我们的测试工程中就可以成功的使用了,但是和静态库有一点不同的是,我们需要在测试工程中General->Embedded Binaries中导入我们这个动态库即可。

链接:https://www.jianshu.com/p/f14553494d88

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

推荐阅读更多精彩内容