一:静态库存在的必要性(应用场景)?
(1)在企业开发中,我们通常把一些核心代码或框架,以静态库的形式提供给开发人员使用,以保证代码的安全性和稳定性。比如我们公司以做银行类项目著名,我们会把一些组件,比如加密键盘组件,自定义控件组件,人脸识别组件提供给其他开发者使用。并且一些组件需要密钥进行关联。
(2)对于一些掌握核心技术的企业(服务提供商),希望把自己的技术供给其他公司使用。例如:ShareSDK,友盟,百度地图,JPush,个推等。
二:库是什么东东?
库是程序代码的集合,是共享程序代码的一种方式
三:根据源代码公开情况,库主要分为两大类
(1)开源库,能够看到具体的实现细节,例如:AFNetworking,SDWebImage,MJfresh,YYKit.
(2)闭源库,看不到源代码,经过编译后以二进制文件的形式存在。主要分为:静态库和动态库。
四:Object-C的代码,在底层都会转为C语言代码,我们看一下C语言的运行过程。以此来了解库的扮演角色
预编译 ---> 编译 ---> 连接 ---> 运行
在“连接”这一环节中,会把系统的一些库和开发者开发的一些库共同连接到C程序中,Object-C只是把“编译”和“连接”推迟到运行阶段而已。
五:静态库和动态库
(1)静态库
存在形式: .a 和 .framework
.framework 和 .a 的区别:.a + .h + 各种资源文件 = .framework
链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝。
(2)动态库
存在形式: .dylib 和 .framework
链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存.
ios8开始已经允许开发者创建动态库,在iOS8之前是不允许的,并且如果用到自制的动态库,不能上传AppStore。
六:静态库的制作,CPU架构( .a + 暴露头文件的方式为例)
第一部分:静态库的制作
举个荔枝:我们把github上别人写好的SDWebImage开源库,转换为静态库,我们该如何操作呢?
演示环境为: Xcode7.3.1
(1)第一步创建一个Cocoa Touch Static Library工程,如下图:
(2)为制作的工程起一个名字(名字只是一个符号,一个代号而已不要去纠结),如下图:
(3)当创建工程完毕后,Xcode默认的文件目录如图:你可以添加任意类到工程文件中以及删除原有的类。
(4)我们把SDWebImage导入工程,command + B 编译 ,如图:
(5)选择Show in Finder 如图:
(6)你会看到如下目录结构:
(7)你可以一个一个的把头文件拷到 .a 文件的目录,当然你肯定认为这样很傻逼,是滴,所以我们通常会这样干,如图:
(8)编译之后,你会看到如下目录:
(9)静态库生成的方式可以在模拟器下生成,也可以在真机下生成,模拟器生成的只能在模拟器下用,真机生成的只能在真机下用,下面我们在真机环境下生成。
(10)点击Show in Finder 看到如下图所示
如果你既要在真机下使用也要在模拟器下使用就需要把模拟器和真机下生成的 .a 库合并。使用以下指令(lipo 是一个命令行工具,在这里使用lipo的目的是联合不同架构的二进制文件到单个输出文件中)
lipo -create 真机下的.a路径 模拟器下的.a路径 -output 合并后的库名称(这个名称随便起,但要以.a结尾)
如果静态库很大,那么合并后就会非常大,所以像百度地图等第三方会提供两个版本的静态库。
第二部分:CPU架构
现在我们已经制作好了静态库,下面我们将新建一个工程进行测试。
首先我们新建一个工程(工程名为StaticLibTest),我们在storyboard中拖一个UIimageView设置好布局并进行联线,工程信息如下:
现在我们运行一下程序(此时是在iphone6 模拟器下运行),点击屏幕结果直接报错,错误信息如下:
-[UIImageView sd_setImageWithURL:placeholderImage:]: unrecognized selector sent to instance 0x7fa020c0f920
明显是方法找不到,由于SDWebImage作者是给UIImageView添加一个分类category,该方法是分类方法。
解决方案:如果静态库中包含了分类Category,在使用静态库的过程中会报“方法找不到”的错误(unrecognized selector sent to instance)
你需要为你的工程配置 Other Linker Flags 为 -ObjC,如下图:
-ObjC为连接标识,增加该标识能把库中的所有OC类和类别都将适当的加载,能够更高效的只包含所需代码。如果你想了解更多,请看官方文档:https://developer.apple.com/library/mac/qa/qa1490/_index.html
当配置好之后重新运行,一切正常。还记得我们生成的静态库是在iphone6模拟器下生成的吗。那么我们把运行环境切换到iphone4s下看一下,如图:
你会发现当点击屏幕时还是报错,而且错误信息和上面的错误信息一样,在Xcode6点几或更早版本,它报的错误可不是这样,它会报以下信息:
undefined symbols for architecture i386
architecture:为架构的意思,指的是CPU架构。
原因:每一台设备都有属于自己的CPU架构,当我们生成一个静态库之后,它所支持的架构就固定了。你可以用以下指令查看静态库所支持的架构:
lipo -info 静态库所在的路径
为了方便读者阅读,我们现把您可能遇到的报错信息以及模拟器和真机支持的CPU架构总结如下。
一:可能遇到的报错信息
undefined symbols for architecture arm64
undefined symbols for architecture armv7
undefined symbols for architecture armv7s
undefined symbols for architecture x86_64
二:模拟器和真机支持的架构
模拟器:
4s —>5 支持 i386 (32位)
5s —->6plus 支持x86_64 (64位)
真机:
iPhone 2G/3G,iPod 1G/2G —>支持 armv6
iPhone 3GS/4/4s,iPad 1G/2G/3G,iPod 3G/4G —> 支持 armv7
iphone5,iphone5c —>支持 armv7s(苹果已经不再对其CPU架构进行优化,可能已经放弃5和5C)
iphone5s,iphone6plus,iphone6s —>支持 arm64
既然作为一个静态库,就应该具备一个库的基本职责,它应该支持所有的架构,当别人用你静态库的时候,你不能告诉他,你的静态库只支持某些机型或只支持某些架构。
解决方案:修改编译参数,如下图:
当你修改完毕后,需要重新生成一个静态库,然后进行测试。发现一切正常。
七:Debug和Release的异同
静态库有四种存在形式: 模拟器-Debug版,模拟器-Release版,真机-Debug版,真机-Release版。
区别:
(1)在包的大小方面:Release(发布版本)会比Debug(调试版本)要小一些。
(2)在执行效率方面:Release(发布版本)会更快一些(因为发布版本的执行代码会进行相应的优化等而Debug版不会对代码优化)。
(3)对于开发人员方面:Release(发布版本)不会包含完整的符号信息(比如打印,调试,当程序崩溃时的提示信息等)而调试版本会包含这些信息,以方便调试开发。
目前我们只有Debug版本,如图:
一般核心技术提供商给我们开发人员提供的都是Release版本,因为你不需要调试他们的代码。那么我们该如何生成Release版本的包呢?
第一步:直接上图
第二步:还是上图吧
然后分别选择模拟器和真机,分别commond + B编译,就会看到对应的静态库,如图:
八:静态库中的资源包(bundle)
Xcode默认编译时会把所有素材文件导入到mainBundle中,为了避免与静态库中的文件相冲突,我们通常把静态库中的资源都放在bundle中。下面我们将讲解一些有关bundle方面的内容。
(1)bundle 你可以理解为资源文件包,通常里面存放一些图片素材,xib文件,配置文本等。包中的资源文件是不参与项目编译的,里面不能包含可执行文件,bundle里面的资源只被解析特定的二进制数据。
(2)为了降低学习成本,本人认为最简单的创建bundle以及向里面存放资源如下(我们新建了一个工程名为AboutBundle,为了演示bundle而建立的):
此处我们暂且起个名为Resourse.bundle,如下:
在代码中如何获取bundle中的资源:
1.获取图片资源(以此处为例):
//这种方式比较简单
UIImage*image = [UIImage
imageNamed:@"Resourse.bundle/你的图片名"];
2.获取里面的控制器
//获取mainBundle
NSBundle * mainBundle = [NSBundle mainBundle];
//获取自己定义的Bundle
NSBundle * resourceBundle = [mainBundle pathForResource:@"Resource" ofType:@"bundle"];
UIViewController *controller = [[UIViewController alloc]initWithNibName:@"controllerName" bundle:resourceBundle];
九:报错信息
有时我们把第三方静态库拖到我们项目中时或你的同事集成第三方静态库,这时你的同事把静态库上传git仓库时,你更新下来发现报一下错误:某某头文件找不到
xxx file not found
解决方案:
$SOURCE_ROOT是Xcode的一个环境变量,该环境变量指向工程根文件夹,Xcode会帮你把工程的实际文件夹代替此变量。