系列文章:OC底层原理系列,OC基础知识系列,Swift底层探索系列,iOS高级进阶系列markdown
前面文章讲了静态库和动态库
,讲的内容都是为了这篇文章作准备,这边咱们就聊一下实际SDK开发
中对静态库和动态库的应用
,平时开发也会用到文章讲的内容。架构
一个多个平台
和架构
的分发二进制库的格式
。在19年推出
Xcode11以上支持
。(不是iOS系统版本
,是Xcode11才有这个功能
)更好
的支持Mac Catalyst
(是为了将iPad上的App更好的移植到ARM芯片的macOS的一个功能)和ARM芯片的macOS
。 专⻔在2019年提出的framework的另外一种先进格式
。单个.xcframework文件提供多个平台的分发二进制文件
;Fat Header
相比,能够按照平台划分
,能够包含相同架构
的不一样平台
的文件;不须要
再经过脚本
去剥离不须要
的架构体系
。写了一个功能名字叫SYTimer
,咱们将它编译成动态库
post
此时须要编译命令: 点击回车,就会发现项目开始编译,最后在
指定文件生成
咱们须要
的文件
ui
编译过程当中
一直生成.o文件
,上面文件就是咱们平时Xcode编译的文件spa
咱们展现下包内容,看下里面的东西debug
SKIP_INSTALL设置为NO就是为了将这个framework拷贝到这个目录下3d
上面咱们已经编译了模拟器架构
,下面咱们来编译成真机架构
调试
红框就是不一样点,一个是
分发平台
,一个名字,点击回车code
此时咱们看到文件夹中已经有两个文件,一个是
模拟器
,一个是真机
,下面咱们来进行合并
orm
在制做SDK过程当中,咱们须要提供的SDK须要即支持真机
,又须要支持模拟器
!此时咱们须要量两个动态库合并
,此时咱们经常使用的就是lipo命令
上面就是lipo的解释:
建立或者操做一个文件
。
咱们先说下胖二进制
,胖二进制
就是多个架构打包到一块儿
,可是就会有一个问题就是打包在一块儿的架构是不能相同
的,下面咱们查一下生成的真机SYTimer可执行文件的架构
咱们发现有两个架构分别为
arm_v7,arm64
!
上面咱们进行打包的时候,只指定了分发平台,并未指定架构,可是为何会出现两个呢?
继续说胖二进制,胖二进制是将
各个动态库
的mach-header放在一块儿
,各个库
的文件
也是肩并肩放在一块儿
,并不是真真意义上的合并
。
下面咱们使用lipo命令进行合并
咱们回车后发现报错了,报错的缘由就是
两个架构都存在arm64
这个主要是由于,咱们在进行模拟器打包时,
Build Settings设置的有arm64
若是非要合并,那么咱们须要将模拟器的x86架构提取出来,获奖arm64架构删除掉,下面咱们来作提取 提取出来后,咱们再作合并
此时具备多架构的SYTimer就生成了
问题:
须要从新处理头文件
包装成一个新的framework
从新签名
以上问题都须要手动操做 还有其余问题,咱们在打包的时候会生成sdYM以及map
dsYM用处应该都懂,而
map
是若是支持了bitcode
,就须要BCSymbolMaps
,不然
即便上传dsYM也没法解析
说完问题,怎么解决,就引出咱们的要讲的内容:苹果在19年推出的XCFramework
下面咱们就来经过命令编译一个XCFramework
按下回车,咱们就发现
生成
了咱们须要的XCFramework
相同架构
,也是能合并
的,并且自动生成相应架构
的framework
,上面咱们说的sdYM没有放进来
,咱们给它放进来-debug-symbols连接文件
须要是绝对路径
,不能
使用相对路径
sdYM和map都给连接了进来
下面咱们来使用这个xcframework
上面讲了,
xcframework
会根据不一样
的平台选择不一样的架构
,而不像传统的framework,都包含在内,下面咱们验证一下
模拟器编译成功
,会生成一个可执行文件,咱们查看下这个可执行文件包内容里的Frameworks,包含的SYTimer.framework的架构
这个是
模拟器架构
arm64架构
进行编译
,查看下可执行文件
这个是
真机架构
经过上面咱们知道xcframework比lipo命令好不少
不用处理头文件
不用再去管重复的架构
能够把调试符号暴露出来
因此建议你们赶快将xcframework用起来吧
建立一个项目,咱们想在这个项目中引入SYTimer.framework 按照咱们以前讲的,连接一个framework的
三要素
指定头文件 -I
指定framework文件所在目录 -F
指定framework名称 -l
写完后,写代码运行,运行成功了
下面咱们运行项目
项目报错,很熟悉的错误,这是由于咱们
并无告诉framework的具体位置
,咱们能够将framework拖进项目
,可是我不想这么作,我想经过xcconfig来设置@rpath
那么这个
设置的@rpath
和报错的/SYTimer.framework/SYTimer拼接
就能找到路径,咱们再运行一次,运行成功
以前说过弱引用
,若是说这个东西在运行
的时候没有定义
,它有是个弱引用
的符号
,那么连接器
自动将它置为null
,也就是它能够容许在运行的时候能够为空
,能够在运行的时候找不到
没有路径运行会报错
把SYTimer.framework置为弱引用
,这样就不会报错了-weak-framework
在连接器中的定义
项目没有报错
,可是打印为null
,此时咱们设置起做用
的,这有什么好处,当项目某一个库找不到的时候也不会报错
此时它
cmd再也不是
:LC_LOAD_DYLIB
而是:LC_LOAD_WEAK_DYLIB
咱们项目中会遇到这样的状况,引入第三方A
,里面有个库叫E
,在引入一个第三方B
,里面也有个库叫E
,此时就会出现冲突
,由于引入了两个E
,那么此时怎么解决呢?下面咱们模拟一下,假设项目中引入两个AFNetworking,咱们来连接这两个库 下面咱们建立xcconfig文件,在文件里进行操做
咱们
连接了两个相同的静态库
,只是名字不一样
,可是在编译期是没有冲突
的,为何?
由于在连接静态库的时候,默认的是
-noall_load
,也就是它会进行代码剥离
,当找到AFNetworking
的时候后面的AFNetworking2
就不会再连接
进去了
-all_load
,再编译报错,提示有
223个符号冲突
,那么怎么解决这个问题呢?
cocoapod的xcconfig文件
,发现OTHER_LDFLAGS有个-ObjC
,是指全部的OC代码都
会被导入,由于AFNetworking和AFNetworking2都为OC代码!那么此时咱们怎么作?咱们可使用-load_hidden
对
静态库文件
能够进行隐藏
,不导出任何东西
下面就是对xcconfig文件从新写
LJNetworkManager
,而后经过cocoaPods导入动态库AFN
LJNetworkManager
的LJAFNetworkingManager.m
来引入动态库AFNetworking
,在LjNetworkManagerTests
,调用LJAFNetworkingManager方法
编译是没有问题
的,可是对LjNetworkManagerTests
进行运行
时报错
的没有找到AFNetworking
,此时咱们看LjNetworkManagerTests的包内容
它是
不存在Frameworks文件夹
的,更不会存在AFNetworking
将AFN
的绝对路径给到LjNetworkManagerTests
但这个问题不能这么解决,因此
绝对路径实不可取
的
copy
的形式将framework拷贝到包里
这个是
cocoapods
中的.sh脚本中代码
,意思就是将项目拷贝到相应的目录
,这也就是为何cocoapods管理的动态库都没这样的问题,还有一种简单的解决办法
让cocoapods对LjNetworkManagerTests一样导入AFN
,而后更新更新
后再运行
,咱们就发现也成功
了
若是我LJNetworkManager
想引用LjNetworkManagerTests
里的代码(反向依赖
)是否可行,以前文章说过dylid在连接的过程当中
会将全部的导出符号放在一块儿
,只要再运行的时候可以找到,就能正常运行
LJNetworkManager引入LJTestAppObject.h
,就须要改一下Pods-LjNetworkManager.debug.xcconfig
LJAFNetworkingManager
中引入LJTestAppObject
,发现识别出来了咱们前面说过项目
只要运行起来
,它本身就能找到,怎么让项目运行起来,以前提过-undefined
,这里直接写
此时运行就成功了,可是有个问题,咱们
随便写的符号
也会被忽略掉
,不会报错!
指定符号-U
此时也会成功
咱们看到此时
调到
了LJTestAppObject方法
,因此咱们上面操做是对的
,实现
了反向依赖
仍是上面的代码,此时咱们将Podfile
中引入AFN的use_frameworks!去掉
AFN的静态库
编译不会报错
LjNetworkManagerTests
可否引入静态库AFN
,答案是能够的,咱们来配置一下连接的是静态库
,只须要找到头文件
就好了,以前文章说过,这里再也不解释。运行时成功的当我给别人提供动态库时
,我不想静态库
的符号暴露出来
,怎么作呢?
连接器
给咱们提供了相应的参数来隐藏:-hidden-l
报错,
未定义的符号
!咱们经过这种方式
来控制静态库符号是否展现
仍是上面的项目,此时咱们将咱们建立的库变成静态库
,而AFN一样也是静态库
此时LjNetworkManagerTests使用LJAFNetworkingManager会不会报错?
咱们分析一下:LjNetworkManagerTests引入静态库没什么问题
,可是组件是个静态库
,它引入静态库
,须要手动集成
才行,咱们编译下:
找不到符号
,此时就须要咱们暴露出去
编译项目
,就成功
了
组件连接静态库
,并没有暴露
给LjNetworkManagerTests,因此须要咱们手动导入
仍是上面的项目,此时咱们导入AFN为动态库
使用静态库
,没什么问题
,可是要使用动态库
就须要将动态库放入LjNetworkManagerTests中
才行。编译项目发现项目报错未找到符号,缘由
LjNetworkManagerTests使用LJAFNetworkingManager
,而LJAFNetworkingManager使用了动态库AFN
,而LjNetworkManagerTests并不知道AFN的位置
,故报错
AFN动态库的位置
由于咱们的
LjNetworkManagerTests不存在AFN的包
,这个问题和咱们将的动态库连接动态库类似
,这里再也不说,你们能够本身试试。
上面的LjNetworkManagerTests你们能够当作一个一个新的App,我是为了省事用LjNetworkManagerTests来代替App
上面咱们讲了动态库
和静态库
间得相互连接
,咱们知道咱们用cocoapods导入AFN是经过use_frameworks
!来肯定导入静态库仍是动态库
,如今有个问题,我想在项目中即导入动态库又导入静态库
,该怎么作
好比此时我想让
AFN做为静态库导入
,而SDWebImage做为动态库导入
简单解释下,就是咱们在
导入podfile的第三方时
,判断若是是AFN
就以静态库方式导入
,不然就按动态库导入
MulitProject.xcworkspace
是基于TestOC而来
,而LjNetworkManager
是带有Podfile的第三方库
Podfile
去同时
给TestOC
和LjNetworkManager添加依赖库
,在LjNetworkManager的Podfile作下面改动:头文件
调试符号
相同架构的处理
weak_import
:动态库
运行时
-> 位置
静态库冲突
:App
-> all_load\ObjC
动态库连接动态库
-> pod\脚本复制
-> rexport动态库
动态库连接静态库
-> 静态库代码不想暴露
-> hidden-l静态库
静态库连接静态库
-> 三要素:1.名称 2.头文件位置 3.包所在位置
静态库连接动态库
-> 编译报错:不知道动态库所在位置
,运行报错:动态库@rpath -> pod\脚本复制
内容比较多,写了一周左右时间!写了5000多字,写的比较细,都是把本身探索过程记录下来
,但愿你们可以按着文章去实操一遍
,这部分也比较无聊,可是这是高阶必走的路。有什么疑问能够在下面留言,也但愿你们多多交流,点赞!