系列文章:OC底层原理系列,OC基础知识系列,Swift底层探索系列,iOS高级进阶系列shell
以前的文章iOS高级进阶系列之-库(上)静态库探索在后面讲到了Dead_Strip
,也说道了-all_load
、-ObjC
以及-force_load
,这篇文章让你们以为-noall_load
、-all_load
、-ObjC
以及-force_load
能够控制Dead_Strip
,其实不是这样的markdown
Build Settings
查找Dead经过上面能够看出来这个
Dead_Strip
和-noall_load
、-all_load
、-ObjC
以及-force_load没有太大关系
,-noall_load、-all_load、-ObjC以及-force_load
只是在链接静态库
的时候起做用
,而Dead_Strip
只是给开发者提供了一种优化代码的方式
架构
上图是test.m的代码,下图是文件,经过脚本将test.m编译成可执行文件。前面的文章介绍过:先将
test.m编译成静态库test.o文件
,再将TestExample.m编译成静态库TestExample.o
,最后经过将test.o链接TestExample.o生成可执行文件
。咱们直接执行脚本函数
当生成可执行文件后,查看macho
信息 咱们能够发现这个里面
没有任何关于TestExample
的内容
,下面咱们对脚本进行改进 post
脚本
默认
的为-noall_load
,它不会将没有用过
的代码放入到静态库
中优化
改完脚本再次执行脚本 ui
发现多了不少东西,并且
未使用的TestExample也导入到项目中
了spa
咱们看下dead_strip的定义 3d
大意就是
删除没有被入口点
或者导出符号使用过的代码
。咱们再改一下脚本code
运行下脚本,看下变化
这里看的是符号表,咱们看下以前代码
咱们发现
global_function
是全局导出符号
,可是未被使用
,可是在导出的符号表
中并没有global_function
,这就是-dead_strip做用
下面咱们调用下global_function方法 在执行脚本,读取符号表
发现
global_function在符号表
中
没有被入口点使用就会被干掉
没有被导出符号使用的也会被干掉
下面咱们将TestExample代码放开,在执行脚本 再来看下符号表
发现都在符号表里
下面该下脚本和代码
问题:
此时符号表仍是否有TestExample内容
发现仍是
存在
的,缘由是OC代码
是动态运行的代码
,因此不敢干掉
,权限不够
shell命令有个能够查看某个代码为何存在的指令:-why_live
查看
global_function为何会存在
解释为何global_function符号存在,
它被从test.o来的,它被main函数使用,main函数又从test.o来
上面讲完后对Dead_Strip和-noall_load、-all_load、-ObjC以及-force_load没有太大关系有了更好的理解了吧
首先咱们来把test.m和AFNetworking动态库进行链接
下图为test.m代码,咱们引入了AFN,并初始化AFHTTPSessionManager
test.m
编译成目标文件
静态库中介绍过命令的用法,这里再也不说明
链接动态库
和链接静态库
使用的指令
都是同样
的
运行test可执行文件
当咱们运行,当即
发生了错误
那究竟为何会出错了?咱们下面探究一下
准备以前的代码
红框的
-dynamiclib
就是告诉编译器
我下面要执行
的是编译动态库
错误
chmod +x ./build.sh
给权限
再次运行脚本
咱们发现仍是报和上面相同的错误,为何呢,是否是脚本命令是否是有问题?
静态库的文章已经说过静态库
是.o的合集
(文章直接演示将.o文件直接改为静态库能够运行),可是动态库
是一个连接编译
的最终产物
。这也就意味着静态库
能够经过连接变成动态库
报错了,就是在连接的过程当中
找不到_TestExample导出符号
动态库的符号表
发现
导出符号表
是空的
,什么都没有
前面介绍
编译期
在链接
的时候默认执行
的是-noall_load
,由于动态库
并没有使用
到.a
的内容
,因此不加载
。那么是否是这个缘由形成的呢?咱们改下脚本
添加-all_load
发现
符号表有内容
了
仍是熟悉的味道,是否是有点无语,问题究竟出在哪里呢?下面就来介绍动态库的另外一个特性
静态库是.o文件的合集
动态库是.o文件连接事后的产物
静态库能够经过连接生成动态库
动态库也就是最终产物,这也是动态库没法合并缘由
下面咱们连连接下动态库,咱们下面来说解LoginApp
怎么去连接动态库SYCSSColor动态库
咱们再看看SYCSSColor里面的内容
xcconfig
文件,具体内容查看iOS高级进阶系列之-项目开发基础(上)多环境配置,Mach-O与连接器这个是连接路径
SYCSSColor.tbd文件
直接拖到项目
中,不在xcconfig中写编译连接
,看看会发生什么经过上面咱们看到了,咱们
引入
了我SYCSSColor
的头文件
,使用了代码而且编译成功
了
咱们只是将SYCSSColor.tbd
文件放入项目
中,那么什么是tbd格式呢?
text-based stub libraries
,本质上就是一个YAML描述的文本文件
。记录动态库
的一些信息
,包括导出的符号
、动态库的架构信息
、动态库的依赖信息
避免
在真机开发过程当中直接使用传统的dylib
。动态库
都是在设备上
,在Xcode上使用基于tbd格式
的伪framework
能够大大减小Xcode的大小
。咱们查看下SYCSSColor.tbd的内容,
exports导出
的意思symbols是符号
的意思,也就意味着11-14行都是导出符号
objc-classes是objc类的集合
经过上面咱们能够知道咱们用脚本去连接库的是用到-L,-l连接
的是符号
,也就是咱们只须要知道符号所在的位置
,不须要知道源码位置
。上面编译经过了,下面就运行看看 上面看到直接崩溃,为何呢?这是由于
动态库是动态连接
的,在运行
的时候,由dyld动态加载动态库
,在加载
的时候,它回去找这个符号真实地址
的时候,找不到
了,而静态库
在编译的时候就已将符号归
放在一块儿,不会动态加载
Xcode的Build Settings
搜索text
原理:就是经过
拼上一些参数
,来扫描Headers里面的头文件
,而后把这个符号写到文件
里去
动态库是.dyld的形式
,其实是没有什么标识的,下面咱们吧生成的删除,重新生成一份叫TestExample
将test.m
和咱们的Frameworks进行连接
这就说明咱们前面制做的
.framework
是成功
的
仍是相同错误,为何还会有这样的错误,这就要
从dyld加载动态库
提及。以下图:
Mach-o
至关于咱们的可执行文件Test
Mach-o
有个专门的LC_LOAD_DYLIB,它里面有咱们
须要用到的动态库路径`找不到
这个路径的时候就会报错
下面咱们看下路径究竟是什么东西
咱们发现咱们Test中一共使用了5个动态库,那么使用哪5个呢?
-A 5 意思就是找到动态库后
多现实5行
咱们看到咱们使用的动态库中name字段就是该动态库路径,可是咱们导入的动态库TestExample路径
只有一个名字
,别的什么都没有,这样确定找不到TestExample。 下面咱们就来解决这个问题
动态库
有一个专门的地方
来保存本身的路径
,也就是说动态库的路径是保存
在本身的Mach-o中
的,下面咱们就来查看下这个路径
-A是向下展现
,向上展现时-B
咱们发现这个地方的路径就没有给对,我要给对正确的路径,外部有个命令来更改路径名称:install_name_tool
咱们看到
install_name_tool
给的解释就是改变更态库
的install name
下面咱们就使用一下,来改变路径
发现报错,这是由于咱们并
没有告诉编译期
往哪里加这个路径
发现此时的路径已经更改了,那咱们再来执行一次build.sh脚本,从新生成test文件,在查看下引入的动态库
咱们发现此时的路径已经改变了,那么此时咱们再运行可执行文件
此时并无报错,成功运行了起来。也说明咱们此时将test.m成功的链接了TestExample动态库
上面咱们讲了是生成动态库后去更改路径的,那么能够在生成前进行更改呢?答案是固然能够,咱们在脚本中更改,咱们使用命令-install_name
,先看下install_name解释
就是添加路径
【问题】
上面咱们看到给到的是一个绝对路径,绝对路径有一个很大的问题,当咱们作好SDK后给别人使用,那么这个路径就会改变,别人就须要更改路径,走着没法调用成功
上面的问题怎么解决,这就须要双方约定好规则,什么规则,看下图:
A使用B,B提供一个
TestExample路径
(Frameworks/TestExample.framework/TestExample),而A给B提供一个使用者路径(也就是test的路径)而后B给拼接起来,此时路径不久完整了嘛!
这就牵扯到@rpath
,@rpath:Runpath search Paths! dyld搜索路径
。运行时@rpath
指示dyld按顺序搜索路径列表
,以找到动态库
。@rpath
保存一个或多个路径的变量! 也就是谁链接我谁给我提供@rpath
,下面咱们直接修改路径
此时咱们看到路径已经修改完了
下面就是须要test给TestExample提供@rpath
了,怎么提供,可执行文件test的mach-o中存一个@rpath
,在链接的时候,将其提供给TestExample。
此时咱们在test可执行文件中
搜索rpath
,发现不存在
,说明此时若是咱们去连接TestExample是找不到位置的,此时须要咱们手动添加
咱们查找一下命令
上面是
替换rpath
,下面的是添加rpath到指定的Mach-o
这时候就完成了,此时咱们从新运行可执行文件
成功了
咱们上面看到咱们打印的path
,依然是一个绝对路径
,那么此时有么有什么办法,改成相对路径呢?这里系统给咱们提供了两个方法:
@executable_path
:表示可执行程序所在的目录
,解析为可执行文件的绝对路径。@loader_path
:表示被加载的Mach-O所在的目录
,每次加载时,均可能被设置为不一样的路径,由上层指定。也就是说若是用了@executable_path
,无论在谁的电脑上它都指向执行程序所在的路径 下面咱们来执行如下,由于咱们已经设置过了rpath,因此须要替换
,上面已经锁了替换方法,来进行操做 此时咱们再看下test的rpath路径
已经将绝对路径修改为
@executable_path
,运行一下test文件,验证一下 咱们看到运行成功,说明咱们的修改是正确的
咱们看下这种状况,test中使用的framework中包含TestExample.framework
,而TestExample.framework的framework中包含TestExampleLog.framework
下面咱们编译一遍,从后往前编译
TestExampleLog.framework
TestExample.framework
咱们发现连接的TestExampleLog.framework的name是不正确的,并且自身的name一样不正确
此时编译完成,但此时运行时报错的,由于
路径不对
,下面咱们来改一下
TestExampleLog.framework
中的脚本
加入name
,地址使用rpath
,再运行脚本
此时的路径就对了
TestExample.framework
的脚本 运行脚本
test是提供rpath路径
的 运行脚本
咱们发现报错了,由于
找不到TestExampleLog的路径
,也就是咱们在TestExample.framework
的脚本中配了本身的路径
,可是并无配TestExampleLog的路径
TestExample.framework的脚本
(截取部分) 运行脚本
下面咱们手动看一下
咱们看到这个路径比较长,下面咱们再来生成下test,若是上面写的都是对的,那就能运行起来
咱们看到这样运行时运行起来了,因此说咱们以前配置的路径是对的
可是有个问题,咱们上面的路径太长了,下面就用到了@loader_path
,上面讲了那么多,一方面为了给你们属性下脚本,第二是让你们更好的理解。
在运行脚本,执行可执行文件
为了加深@executable_path印象
,咱们能够看看cocoaPods的xcconfig
文件
此时的
name
是一个名称
当咱们编译代码,看下可执行文件的包内容,咱们发现动态库
都会存放在framework文件
中,也就是上面设置的文件里
到此最难的连接动态库的一种方式已经说完了,连接动态库
因为动态加载
,因此必定要指定好路径
,否咋就会报错。
install_name_tool
来更改路径
@rpath
-rpath lod new 是替换路径
,-add_rpath是将路径添加到指定位置
@executable_path
和@loader_path
上面讲了能够替换路径
,那么就想到了破解
,由于咱们能够改变路径
,将咱们本身写的动态库
做为目标软件的依赖
,达到破解的目的
。咱们这么作事改变了Mach-o
,以前讲过Mach-o只有签名
后才会被苹果系统认可
,因此再破解软件是都会调用一句命令:code sign --force --deep --sign -
这句命令也就是强制打一个签名
当咱们作动态库时,能够在Xcode中设置咱们上面说的命令 咱们在搜下
rpath
咱们上面的路径设置均可以在Xcode去处理
咱们上面说的test
是能够使用TestExample的方法
,那么他test
能够使用TestExampleLog的代码
吗? 咱们看下导出符号 咱们看到这里面
没有TestExampleLog的导出符号
,因此不能使用
,那么怎么才可以使用呢?在以前在讲符号的时候,说过从新导出符号
,下面说下命令:reexport
从新导出framework
从新导出TestExampleLog
,运行脚本
多了一个
从新导出符号TestExampleLog
,此时就可使用TestExampleLog代码
了
咱们更改的代码以及打印
都能正常运行
,验证了咱们上面改的东西
动态库写的内容比较多,写了一周多时间!写了将近6000字,写的比较细,都是把本身探索过程记录下来
,但愿你们可以按着文章去实操一遍
,这部分也比较无聊,可是这是高阶必走的路,在过程当中学会shell语言
,会写脚本
,这部分都是基础
,后面会介绍项目高阶应用:静态库合并,动态库优化
等内容。有什么疑问能够在下面留言,也但愿你们多多交流,点赞!