iOS高级进阶系列之-库(下)动态库和静态库项目应用

系列文章:OC底层原理系列OC基础知识系列Swift底层探索系列iOS高级进阶系列markdown

前言

前面文章讲了静态库和动态库,讲的内容都是为了这篇文章作准备,这边咱们就聊一下实际SDK开发对静态库和动态库的应用,平时开发也会用到文章讲的内容。架构

XCFramework

XCFramework简介

  • 1.是苹果官方推荐的、支持的,能够更方便的表示一个多个平台架构分发二进制库的格式。在19年推出
  • 2.须要Xcode11以上支持。(不是iOS系统版本,是Xcode11才有这个功能
  • 3.是为更好支持Mac Catalyst(是为了将iPad上的App更好的移植到ARM芯片的macOS的一个功能)和ARM芯片的macOS。 专⻔在2019年提出的framework的另外一种先进格式

和传统的framework相比

  • 1.能够用单个.xcframework文件提供多个平台的分发二进制文件;
  • 2.与Fat Header相比,能够按照平台划分,能够包含相同架构不一样平台的文件;
  • 3.在使用时,不须要经过脚本剥离不须要架构体系

动态库合并

写了一个功能名字叫SYTimer,咱们将它编译成动态库 image.pngpost

模拟器架构

此时须要编译命令: image.png 点击回车,就会发现项目开始编译,最后在指定文件生成咱们须要文件 image.pngui

编译过程当中一直生成.o文件,上面文件就是咱们平时Xcode编译的文件spa

咱们展现下包内容,看下里面的东西debug

image.png

SKIP_INSTALL设置为NO就是为了将这个framework拷贝到这个目录下3d

真机架构

上面咱们已经编译了模拟器架构,下面咱们来编译成真机架构 image.png调试

红框就是不一样点,一个是分发平台,一个名字,点击回车code

image.png

此时咱们看到文件夹中已经有两个文件,一个是模拟器,一个是真机,下面咱们来进行合并orm

合并

在制做SDK过程当中,咱们须要提供的SDK须要即支持真机,又须要支持模拟器!此时咱们须要量两个动态库合并,此时咱们经常使用的就是lipo命令 image.png

上面就是lipo的解释:建立或者操做一个文件

胖二进制

咱们先说下胖二进制胖二进制就是多个架构打包到一块儿,可是就会有一个问题就是打包在一块儿的架构是不能相同的,下面咱们查一下生成的真机SYTimer可执行文件的架构 image.png

咱们发现有两个架构分别为arm_v7,arm64

上面咱们进行打包的时候,只指定了分发平台,并未指定架构,可是为何会出现两个呢?

  • 这是由于在打包的时候,会执行Xcode项目中Build settings的设置。

image.png 继续说胖二进制,胖二进制是将各个动态库mach-header放在一块儿各个库文件也是肩并肩放在一块儿,并不是真真意义上的合并

合并

下面咱们使用lipo命令进行合并 image.png 咱们回车后发现报错了,报错的缘由就是两个架构都存在arm64 image.png

这个主要是由于,咱们在进行模拟器打包时,Build Settings设置的有arm64

若是非要合并,那么咱们须要将模拟器的x86架构提取出来,获奖arm64架构删除掉,下面咱们来作提取 image.png 提取出来后,咱们再作合并 image.png

此时具备多架构的SYTimer就生成了

问题:

  • 1.生成的SYTimer须要从新处理头文件
  • 2.须要包装成一个新的framework
  • 3.须要从新签名

以上问题都须要手动操做 还有其余问题,咱们在打包的时候会生成sdYM以及map image.png

dsYM用处应该都懂,而map是若是支持了bitcode,就须要BCSymbolMaps不然即便上传dsYM也没法解析

说完问题,怎么解决,就引出咱们的要讲的内容:苹果在19年推出的XCFramework

使用XCFramework

下面咱们就来经过命令编译一个XCFramework image.png 按下回车,咱们就发现生成了咱们须要的XCFramework image.png

  • 咱们能够看到相同架构,也是能合并的,并且自动生成相应架构framework,上面咱们说的sdYM没有放进来,咱们给它放进来

image.png

  • 注意的是-debug-symbols连接文件须要是绝对路径不能使用相对路径

image.png

  • 咱们上面说的sdYM和map都给连接了进来

下面咱们来使用这个xcframework

  • 1.直接将SYTimer.xcframework拖入项目

image.png

  • 2.引入头文件,建立属性

image.png

  • 3.选择模拟器进行编译

image.png 上面讲了,xcframework根据不一样平台选择不一样的架构,而不像传统的framework,都包含在内,下面咱们验证一下

  • 1.根据模拟器编译成功,会生成一个可执行文件,咱们查看下这个可执行文件包内容里的Frameworks,包含的SYTimer.framework的架构

image.png image.png 这个是模拟器架构

  • 2.换成arm64架构进行编译,查看下可执行文件

image.png image.png 这个是真机架构

总结

经过上面咱们知道xcframework比lipo命令好不少

  • 1.不用处理头文件
  • 2.不用再去管重复的架构
  • 3.能够把调试符号暴露出来

因此建议你们赶快将xcframework用起来吧

项目实际应用

弱引用

建立一个项目,咱们想在这个项目中引入SYTimer.framework image.png 按照咱们以前讲的,连接一个framework的三要素

  • 1.指定头文件 -I

image.png

  • 2.指定framework文件所在目录 -F

image.png

  • 3.指定framework名称 -l

image.png 写完后,写代码运行,运行成功了 image.png 下面咱们运行项目 image.png 项目报错,很熟悉的错误,这是由于咱们并无告诉framework的具体位置,咱们能够将framework拖进项目,可是我不想这么作,我想经过xcconfig来设置@rpath image.png 那么这个设置的@rpath报错的/SYTimer.framework/SYTimer拼接就能找到路径,咱们再运行一次,运行成功 image.png

弱引用符号

以前说过弱引用,若是说这个东西在运行的时候没有定义,它有是个弱引用符号,那么连接器自动将它置为null,也就是它能够容许在运行的时候能够为空能够在运行的时候找不到

  • 上面讲若是没有路径运行会报错

image.png

  • 咱们能够把SYTimer.framework置为弱引用,这样就不会报错了

image.png

  • 咱们看下-weak-framework连接器中的定义

image.png

  • 跟上面解释一致,再也不过多叙述,直接运行,项目看会不会报错

image.png 项目没有报错,可是打印为null,此时咱们设置起做用的,这有什么好处,当项目某一个库找不到的时候也不会报错

  • 那么此时Mach-o作了什么,咱们能够看一下

image.png 此时它cmd再也不是LC_LOAD_DYLIB 而是:LC_LOAD_WEAK_DYLIB

连接冲突

咱们项目中会遇到这样的状况,引入第三方A,里面有个库叫E,在引入一个第三方B,里面也有个库叫E,此时就会出现冲突,由于引入了两个E,那么此时怎么解决呢?下面咱们模拟一下,假设项目中引入两个AFNetworking,咱们来连接这两个库 image.png 下面咱们建立xcconfig文件,在文件里进行操做 image.png

  • 写完后咱们编译,没有问题,下面咱们引入头文件进行操做,以后编译也没有问题。

image.png 咱们连接了两个相同的静态库,只是名字不一样,可是在编译期是没有冲突的,为何?

由于在连接静态库的时候,默认的是-noall_load,也就是它会进行代码剥离,当找到AFNetworking的时候后面的AFNetworking2不会再连接进去了

  • 若是换成-all_load,再编译

image.png

报错,提示有223个符号冲突,那么怎么解决这个问题呢?

  • 咱们看下cocoapod的xcconfig文件,发现OTHER_LDFLAGS有个-ObjC,是指全部的OC代码都会被导入,由于AFNetworking和AFNetworking2都为OC代码!那么此时咱们怎么作?咱们可使用-load_hidden

image.png

静态库文件能够进行隐藏不导出任何东西

下面就是对xcconfig文件从新写 image.png

  • 运行成功

image.png

动态库连接动态库

  • 1.咱们建立一个Fremework叫LJNetworkManager,而后经过cocoaPods导入动态库AFN

image.png

  • 2.此时咱们在动态库LJNetworkManagerLJAFNetworkingManager.m来引入动态库AFNetworking,在LjNetworkManagerTests调用LJAFNetworkingManager方法

image.png

  • 3.此时编译是没有问题的,可是对LjNetworkManagerTests进行运行报错

image.png

  • 4.缘由是并没有找到AFNetworking,此时咱们看LjNetworkManagerTests的包内容

image.png

它是不存在Frameworks文件夹的,更不会存在AFNetworking

  • 5.如何解决呢?咱们能够将AFN绝对路径给到LjNetworkManagerTests

image.png

  • 6.此时再运行就会发现都成功了

image.png

但这个问题不能这么解决,因此绝对路径实不可取

  • 7.咱们能够经过copy的形式将framework拷贝到包里

image.png

这个是cocoapods中的.sh脚本中代码,意思就是将项目拷贝到相应的目录,这也就是为何cocoapods管理的动态库都没这样的问题,还有一种简单的解决办法

  • 8.让cocoapods对LjNetworkManagerTests一样导入AFN,而后更新

image.png

  • 9.更新后再运行,咱们就发现也成功

image.png image.png

拓展

若是我LJNetworkManager想引用LjNetworkManagerTests里的代码(反向依赖)是否可行,以前文章说过dylid在连接的过程当中会将全部的导出符号放在一块儿,只要再运行的时候可以找到,就能正常运行

  • 1.暴露Header

image.png

  • 2.导入头文件,在LJNetworkManager引入LJTestAppObject.h,就须要改一下Pods-LjNetworkManager.debug.xcconfig

image.png

  • 3.在LJAFNetworkingManager引入LJTestAppObject,发现识别出来了

image.png

  • 4.运行后发现报错,找不到这个符号

image.png

咱们前面说过项目只要运行起来,它本身就能找到,怎么让项目运行起来,以前提过-undefined,这里直接写

image.png

此时运行就成功了,可是有个问题,咱们随便写的符号也会被忽略掉,不会报错!

  • 5.指定符号-U

image.png

此时也会成功

  • 6.验证

image.png

咱们看到此时调到LJTestAppObject方法,因此咱们上面操做是对的实现反向依赖

动态库连接静态库

仍是上面的代码,此时咱们将Podfile中引入AFN的use_frameworks!去掉 image.png

  • 1.更新Podfile后,引入的就是AFN的静态库

image.png

  • 2.此时编译不会报错

image.png

  • 3.此时咱们LjNetworkManagerTests可否引入静态库AFN,答案是能够的,咱们来配置一下

image.png

  • 4.上面设置完就能够了,由于咱们连接的是静态库,只须要找到头文件就好了,以前文章说过,这里再也不解释。运行时成功的

image.png

拓展

当我给别人提供动态库时,我不想静态库符号暴露出来,怎么作呢?

  • 1.连接器给咱们提供了相应的参数来隐藏:-hidden-l

image.png

  • 2.此时咱们再编译

image.png

报错,未定义的符号!咱们经过这种方式控制静态库符号是否展现

静态库连接静态库

仍是上面的项目,此时咱们将咱们建立的库变成静态库,而AFN一样也是静态库 image.png

此时LjNetworkManagerTests使用LJAFNetworkingManager会不会报错?

咱们分析一下:LjNetworkManagerTests引入静态库没什么问题,可是组件是个静态库,它引入静态库,须要手动集成才行,咱们编译下: image.png

找不到符号,此时就须要咱们暴露出去

  • 1.在Build settings

image.png

  • 2.此时在编译项目,就成功

image.png

组件连接静态库,并没有暴露给LjNetworkManagerTests,因此须要咱们手动导入

静态库连接动态库

仍是上面的项目,此时咱们导入AFN为动态库 image.png

  • LjNetworkManagerTests使用静态库没什么问题,可是要使用动态库就须要将动态库放入LjNetworkManagerTests中才行。编译项目发现项目报错

image.png

未找到符号,缘由LjNetworkManagerTests使用LJAFNetworkingManager,而LJAFNetworkingManager使用了动态库AFN,而LjNetworkManagerTests并不知道AFN的位置,故报错

  • 告诉LjNetworkManagerTests须要使用的AFN动态库的位置

image.png

  • 运行项目,不成功

image.png

由于咱们的LjNetworkManagerTests不存在AFN的包,这个问题和咱们将的动态库连接动态库类似,这里再也不说,你们能够本身试试。

上面的LjNetworkManagerTests你们能够当作一个一个新的App,我是为了省事用LjNetworkManagerTests来代替App

拓展

Podfile导入多样库

上面咱们讲了动态库静态库间得相互连接,咱们知道咱们用cocoapods导入AFN是经过use_frameworks!来肯定导入静态库仍是动态库,如今有个问题,我想在项目中即导入动态库又导入静态库,该怎么作 image.png

好比此时我想让AFN做为静态库导入,而SDWebImage做为动态库导入

  • 在Podfile作以下操做

image.png

简单解释下,就是咱们在导入podfile的第三方时,判断若是是AFN就以静态库方式导入,不然就按动态库导入

image.png

经过podfile同时导入多个项目

image.png

MulitProject.xcworkspace基于TestOC而来,而LjNetworkManager带有Podfile的第三方库

  • 咱们经过LjNetworkManager的Podfile同时TestOCLjNetworkManager添加依赖库,在LjNetworkManager的Podfile作下面改动:

image.png

  • 更新下Podfile

image.png

简单总结

XCFramework

  • 1.头文件
  • 2.调试符号
  • 3.相同架构的处理

项目应用

  • 1.weak_import动态库 运行时 -> 位置
  • 2.静态库冲突App -> all_load\ObjC
  • 3.App -> 动态库连接动态库 -> pod\脚本复制 -> rexport动态库
  • 4.App -> 动态库连接静态库 -> 静态库代码不想暴露 -> hidden-l静态库
  • 5.App -> 静态库连接静态库 -> 三要素:1.名称 2.头文件位置 3.包所在位置
  • 6.App -> 静态库连接动态库 -> 编译报错:不知道动态库所在位置,运行报错:动态库@rpath -> pod\脚本复制

写在最后

内容比较多,写了一周左右时间!写了5000多字,写的比较细,都是把本身探索过程记录下来,但愿你们可以按着文章去实操一遍,这部分也比较无聊,可是这是高阶必走的路。有什么疑问能够在下面留言,也但愿你们多多交流,点赞!

相关文章
相关标签/搜索