美文网首页
组件化模块(四): 静态库的封装

组件化模块(四): 静态库的封装

作者: 文艺女青年的男人 | 来源:发表于2018-07-14 15:40 被阅读0次

    当我们看好多第三方的插件的时候,会发现好多.a/.framework的静态库,之所以这些大厂这么做,其实是为了方便在集成完毕之后,快速的编译,最重要的是看不到自己的源码,并且保护我们的代码。并且在MRC阶段的代码,如果想要在ARC下使用,那么确实需要进行ARC兼容,但是我们进行编译之后,并不需要考虑这些问题,直接引用这个编译好的静态库就行,并不需要兼容ARC,确实好处多多。

    首先讲解如何创建.a的二进制文件

    1.创建一个静态库工程

    创建静态库

    2.由于不同的模拟器以及真机对应着不同的架构,

    模拟器:4-5:i386   5s-iphoneX:x86-64

    真机:3gs-4s: armv7(兼容7s)   5-5c: armv7s   5s-iphoneX:arm64

    为了适配所有的CPU架构我们设置如下,Build Active Architecture Only  YES表示只编译某些比较主流的架构,所以要想都进行编译,我们需要对所有的都编译

    注意如果你用的是比较新的xcode版本,比如xcode10,那么由于并没有自带比较老的模拟器,比如4/4s等,那么生成出来的静态库架构并不会包含i386或者armv7,如果想兼容4/4s等比较久的机型,需要下载老的模拟器版本

    设置允许的架构类型

    3.查看生成的.a文件架构架构

    在终端中cd 到.a文件的父文件

    输入命令 lipo  -info  静态库名称.a

    输出当前所有支持的架构

    架构类型

    4.暴露.h 文件

    暴露.h

    5.默认生成的.a文件模拟器和真机是分开的,所以需要合成同时支持真机和模拟器的.a

    lipo -create 模拟器中.a文件地址 真机中.a文件地址 -output  新的文件名.a

    合成

    6.生成realease版本的静态库(上面生成的是debug版本)

    静态库

    然后在项目中切换真机/模拟器,command+B,就会在.a文件的根目录发现release/debug、真机/模拟器版本信息

    所有的.a文件

    当我们应用.a文件的时候,只需要将import  .h文件进来就可以直接使用静态库文件里的方法了。

    接下来我们用framework来生成静态库

    .a文件是一个纯二进制文件,.frameWork中除了有二进制文件还有其他的资源文件;.a文件必须有.h文件配合使用,frameWork文件可以直接使用;framework静态库就类似一个文件夹,所有的子类文件都要放到framework中,包括可执行文件frameWork和.h以及资源文件。可以这么总结.a+.h+sourceFile(资源文件)=framework,所以相比.a文件来说,framework更加易于管理,所以本人更倾向与生成framework静态库

    同样在新建工程中选择

    生成

    2.项目中添加一个工具类Tool,当Command+B编译之后,在framework中发现只有frameWork.h的头文件,并没有我们想要的Tool.h,所以我们需要对Tool.h暴露,将暴露到外界的.h文件放到public目录下,如果不用frameWork.h主头文件,可以将其删除

    暴露.h

    3.检查可执行文件支持的CPU架构

    注意cd到.frameWork,然后检查目录下frameWork可执行文件中的架构,发现是x86_64,由于当前是在模拟器上进行的编译,所以我们需要兼容所有的模拟器/真机类型.

    支持的种类

    修改当前active Architecture Only的类型为No,表示所有的模拟器/真机架构都需要编译

    编译架构

    接下来我们需要在XCode中选择设备处设置为Generic ios Device,编译后的结构可以在项目中的Products->Framework.framework包中找到编译之后的结构,包含真机和模拟器两部分.

    选择设备

    4.适配所有的CPU架构(和上边生成.a文件一样)

    5.生成release版本的.framework静态库,而不是debug版本的静态库注意:当前5到7步是生成了动态库,可以直接看7步中的原因,进行相应的步骤调整。这样写是为了更好的记录遇到的问题

    6.合成适配模拟器和真机的静态库

    注意合成的是静态库目录下的可执行文件(frameWork)的合并。

    将地址拖过来就行

    lipo -create 模拟器中可执行文件地址 真机中可执行文件地址 -output  新的文件名.framework

    将生成的frameWork,替换掉之前release版本里的可执行文件,那么这个frameWork就可以用了。

    7.修改frameWork(刚生成的时候其实为动态库)为静态库

    查看文件 引用framWork

    防止崩溃的发生,我们需要将frameWork动态库引用进Embedded Binaries

    所以,当我们生成frameWork的时候就应该生成静态库。所以在生成frameWork工程中,

    修改framework类型为静态库

    将Dynamic Library修改为 Static Library。所以上边的第5到第7个步骤中我们需要重新进行生成。(删除工程中Library Framework和 Embedded Binaries 中的动态库文件)重新导入工程进行测试,发现不将framework引入Embedded Binaries,也是可以正常运行的,那么现在生成的是静态库了。

    记录一些问题:

    (1).图片资源的导入问题?(framework和.a图片的导入方式是一样的)

    拿生成.a静态库举例,添加资源文件,将图片放入Tool.h同级目录下,编译,会发现我们编译后的包中,并没有我们的资源图片,我们需要将图片文件添加到BuildPhases->CopyFiles中+进来,才会放到我们编译后的文件中,包括三部分.a+.h+资源图片,那么这个时候会出现一个问题:

    将我们编译好的.a+.h+资源文件作为一个静态库放入我们的主项目中,如果我们的主项目中已经有一个同名图片,然后对主项目进行编译后,发现包中只有一个同名的图片.

    为了解决这个问题,我们将图片放到一个文件夹中,将这个文件夹放到和.a/.h同级目录下,当我们在主工程引用这个静态库的时候,需要将这个文件夹拖入主工程,但是我们用两个选择Create Group/Create folder references,由于我们将静态库导入到主工程,其实包括资源文件/.h/.a都已经编译好了,所以并不需要主工程对当前的资源文件做出引用,应该选择Create folder references,这样会将资源文件/.a/.h都加入到主工程的同级目录下,否则会发现资源文件不见了.

    但是如果选择了Create Group,那么就会出问题,所以我们将资源文件放到bundle中进行存放(),bundle中的资源并不会因为这两个选项选错了就会把资源文件位置放错,一直会放到同级目录下.

    (2).如果用户导入的头文件过多怎么办?

    我们自己先建立一个主头文件,我们将所有的头文件都导入到主头文件中,我们直接import主头文件就行

    (3).静态库如何测试?

    .a文件如果交给主工程测试无法定位问题所在

    所以定义一个复合工程,进行单元测试,在Targets中点击+号,选择Framework & Library 中的CocoaTouch Framework,新建一个Framework工程

    添加Testframe复合工程

    比如我们想要在A业务模块中调试代码,那么我们将A业务模块代码放到主工程中,调用我们想要编译成静态库的代码B,将B(以Tool类为例)

    添加到Testframework中

    在复合工程FrameworkTests中引入我们的静态库类,然后再主工程中调用Tool.h类进行测试,如果没问题,那么在Targets点中我们的复合工程Testframework,配置生成静态库的某些项,编译完之后,再Products中查找Testframework并查看包的内容,生成合并的release静态库.

    (4).当我们将组建进行二进制化之后,那么导入方式将会改变,例如#import 'Tool.h'  #import <Framework/Tool.h>,如何避免多地方的改动?

    我们可以将.h文件剥离出来,独立于framework之外,引用的时候直接调用#import 'Tool.h',实现的时候还是在framework中进行实现

    (5).如何将生成的所有的架构文件删除某个架构,该如何操作?比如我们不需要支持i386

    第一种:那么我们分别对其他的架构生成静态库,然后将静态库合并

    第二种:直接在生成的静态库中移除某一个架构, cd到当前静态库的上一级,命令     

     lipo -remove i386 文件地址.a  -output  生成的名字.a

    如果只要某一个架构的文件 比如arm64:

    lipo -thin arm64 文件地址.a -output 生成名.a

    接下来我们将framework放到远端仓库

    以之前创建的一个WKDownLoad私有库为例首先创建一个Framework 项目WKDownloadLb,将WKDownLoad中的代码放到WKDownloadLb中,并暂时删除自带的WKDownloadLb.h文件,Xcode配置相应的生成framework的某些项,mach-o-Type/Build Active Architecture Only/run->release

    生成的framwork项目

    接下来,为了能够方便的查找我们生成framework位置,需要对xcode进行配置File->Project Settings

    Project Settings 具体存放位置

    将framework存放到相对于workspace的同级目录下的products文件下,开始进行编译,然后将framework进行合并,合并完之后,我们可以将WKDownloadLb.framework放到Products中,把iphoneos/iphonesimulator删掉

    编译后的路径

    接下来将原来的WKDownLoad中的podspec文件拷贝一份放到我们的WKDownloadLb项目中,用于进行pod时的查找,并修改podspec,包括库的名称/homepage/source/,在初始化coding.net一个项目的时候,不要设置git.ignore,将coding.net中的homepage/source分别放到podspec对应位置

    为了外界不用改动引入我们.h文件的方式,我们做一层兼容,在framework外把所有的.h文件都加进来(真实情况是将所有.h文件都加都一个文件中,只引用某一个文件就行),在根目录下创建一个文件夹,把所有的.h文件都拷贝一份放到这里.

    .h的header文件夹

    修改source_files/s.vendored_frameworks,其中vendored_frameworks其实是指明我们framework的地址专门存在的,由于已经将framework放到了products中,所以地址更短了;source_files是除了framework之外的而外信息,就是我们的暴露header

    framework/header设置

    修改完之后,按照

    git add .

    git status

    git commit -m '标记'

    git remote

    git remote add origin 地址

    git push origin master

    git tag -a 'tag值' -m '打标签'

    git push --tags

    pod lib lint   

    (1)第一种错误

    pod lib lint 第一种错误

    发现好多小伙伴报这种错误,

    解决方案:

    pod repo push 私有spec xxxxxx.podspec --allow-warnings --skip-import-validationpod spec lint xxxxxx.podspec --skip-import-validation

    参考地址

    这个问题困扰了我很久,终于解决了,感谢小伙伴的贡献啊.

    (2)第二种错误

    pod lib lint 第二种错误

    是由于tag没提交远端或者podspec中tag不一致导致的.

    最终方案

    我们现在远程仓库有一个原生代码组件和一个静态库组件,那么我们这样有一个弊端,需要改两个组件库才能将两个库中代码修改完毕,并且需要进行pod文件名称的修改,很不方便

    能不能合并到一个里边那?答案是肯定滴

    再原来pod lib create 创建的私有库demo中创建一个framework的复合工程

    复合工程

    将framework复合工程中的部分中引入对原来类的引用,注意只是引用,并不拷贝,这样就能保证修改的是一份代码.

    引用

    生成的framework复合工程部分,引用完之后代码结构

    代码结构

    为了生成静态库,需要在复合项目的build Setting工程目录下修改相应的配置,release/mach-o/build avtive/修改暴露外界的.h到Public中等,然后编译生成静态framework库,合并真机和模拟器的可执行文件,放到原来组件的Classes的同级目录下

    products同级目录下

     接下来,修改podspec文件,并设置静态库暴露在外的头文件以及framework静态库

    设置原来组件的podspec

    修改暴露的source_files为暴露classes中的.h,添加引用的framework地址,这样就算设置完毕了.

    接下来我们将修改后的组件上传到原来未二进制化时的私有库中

    进行上传...

    在其他地方调用本组件,会发现已经二进制化了😆,搞定

    最终将上传到私有库的framework进行pod install,但是报了一个错误

    pod install

    发现是由于在pod file中打开了use_frameworks!,注掉就行

    我们可以发现:这样并不需要添加一个新的pod依赖,并且通过pod update --no-repo-update就可以更新我们的远端私有库,又节省了远端私有库的资源

    最后兼容源码和静态库两种类型

    pod 中是可以设置环境变量的,比如 xxx pod install,xxx就是我们的环境变量

    安装的时候 IS_SOURCE=1 pod install,就会把原生代码加进来了

    兼容原生和二进制

    注意的地方:

    1.如果我们生成的是.a的静态库,并不是framework静态库,那么需要将s.vendored_framework换成s.vendored_libraries就行了

    2.如果原来是framework,再IS_SOURCE=1 pod install到原生,那么会发现安装进来的私有库.m文件找不到了,这时需要先将之前的缓存清掉 pod cache clean --all,再将pods文件夹全部删除,这个时候再执行IS_SOURCE=1 pod install

    3.如果我们的私有库都用这个环境变量控制,就能起到统一管理私有库源码或者二进制文件导入,但是这个时候有一个问题,如何只设置某一个为原生代码?其他都为二进制

    可以给每一个私有库单独设置一个变量,表示当前私有库需要设置二进制 - WKDownload

    单独设置为原生

    执行WKDownload=1 pod install ,这样,私有库中的所有代码都设置为二进制形式,除了WKDownload为原生

    4.私有库中有依赖,那么怎么处理

    不应该包含依赖的xxx.a/framework文件

    并且比如包含很多subspec,那么我们需要将所有的都放到一个framework中

    快速生成framework

    最后,为了方便,我们很多时候可以利用cocoapods-packager来快速完成framework打包,但是必须注意,我们需要将原来的源码已经用上传到托管平台,并且已加了tag。

    1.安装package

    安装

    2.根据生成静态库

    静态库生成

    3.查看支持的类型

    支持的类型

    4.把不需要的类型删除

    删除

    根据路径下的库中的podspec文件,生成相对应的framework,并且支持所有的机型,个人比较倾向于这个工具。

    包含subspec的静态库的封装

    如果有framework里有依赖,那么我们不能用自动化的cocoapods packager打包framework框架里.

    组合项目里创建target framework来进行静态库的封装,打包完之后其实是不包含我们的第三方依赖的,

    我们的复合项目中想要用第三方的框架中的代码,那么需要将我们创建的target放入pod中,根据pod 文件的修改

    pod 文件添加framework类

    修改podspec  去掉之前色subspec,将所有的代码看成一个整体

    修改podspec 文件里添加source_files/vendored_frameworks

    去掉  库名称.dependency      '依赖库名称'

    然后按照framwork的封装进行提交到私有库

    欢迎关注我的公众号,专注iOS开发、大前端开发、跨平台技术分享。

    iOS开发之家

    相关文章

      网友评论

          本文标题:组件化模块(四): 静态库的封装

          本文链接:https://www.haomeiwen.com/subject/plcypftx.html