iOS组件化之动态库和静态库

动态库形式:.dylib和.framework
静态库形式:.a和.framework

对系统内存影响

静态库:之所以叫做静态,是因为编译时(确切的说是链接时),静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝(图1所示)

系统动态库:与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。也就是说链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用(因此动态库也被称作共享库),节省内存(图2所示)
但是:运行时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行

自己创建的动态库与系统动态库的区别
我们创建的动态库是在自己应用的 .app 目录里面,只能自己的 App Extension 和 APP 使用。而系统的动态库是在系统目录里面,所有的程序都能使用。

图中的绿框表示app的可执行文件


a0l5w-wch79.jpg
对app体积影响

同样的代码生成静态库文件体积大,动态库文件体积小。
静态库:.o文件的集合(每个.o文件都是各自的代码,而每个.o文件都有各自的mach header等)(静态库=>header+.a+签名+资源文件)

动态库:所有文件整合在一起,类似只有一个.o文件(简单的说就是会少很多杂七杂八的文件)(动态库=>header+.dylb+.a+签名)
但是链接到App里面后,可能使用静态库的App文件体积更小。因为使用静态库的时候可以通过链接器标志(-objc,-all_load ,-force_load等)来拷贝需要的代码。而动态库需要拷贝整个所有代码。但-Objc或-all_load也会将所有代码都拷贝进去。
上面说的动态库都是自己创建的动态库,我们自己做出来的 Framework 哪怕是动态的,最后也还是要拷贝到 App 中(App 和 Extension 的 Bundle 是共享的),因此苹果又把这种 Framework 称为Embedded Framework。

链接器标志查找方式:终端输入man ld
查找某个命令:在:后面输入/+命令比如/-force_load

库的使用

1、静态库链接动态库

以下只是用于学习举例,平常做组件化的时候podspec里面可以直接dependency

创建一个Static Library,然后使用cocopod添加一个三方库。我这边使用YYModel,大家随意。

截屏2021-01-25 下午3.39.25.png

在.h文件中#import 然后run一下,build success。好像没什么问题。接下来就看看这个Static Library能不能用吧
创建一个单测,模拟一下在App中使用

1、在静态库的.m文件中创建一个test方法

- (void)test {
    NSLog(@"test nslog");
    YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:[NSString class]];
}

2、在单测里面调用一下,然后运行单测。结果crash报错 'YYModel/YYModel.h' file not found'为啥在编译阶段就直接报错捏。

- (void)testExample {
    stStatic *t = [[testStatic alloc] init];
    [t test];
}

我们当前的库是静态库,我们现在使用的YYModel是动态库。因为podfile里面使用了use_frameworks!
成功链接一个库的条件:
1、头文件
2、库名称
3、库所在的位置

那么我们能正常编译我们的静态库。原因就是:我在上面说了,静态库就是一个.o文件的集合,编译的时候只需要库的头文件。所以我们通过pod导入YYModel的时候已经知道了YYModel的头文件,因此编译我们静态库没有问题

然而我们单测(模拟App)去链接静态库的时候,我们知道我们静态库的头文件,库名称,位置,但是App只能拿到YYModel的头文件,名称会Autolinking机制拿到。但是拿不到位置。

找到问题就好解决了。无非就是让xcode知道我们YYModel在那个位置,在XCTest的Build Setting里面找到Search Paths,将路径写到Framework Search Paths。看下图,我们YYModel的路径和我们ipa包路径是一致的,如下图1。那么将正确的路径写到Framework Search Paths里面就行了。路径么就是${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/YYModel。不清楚看这里https://www.jianshu.com/p/6bebede0b02d

图1.png

然后我们再跑一下我们单测的代码。就编译成功了。但是问题并没有那么简单,单测失败了...报错信息如下图2

图2.png

问题就是@rpath/YYModel.framework/YYModel这个路径下面没有找到我们的YYModel库。(关于@rPath看这里https://www.jianshu.com/p/93c2f50ada3a)
截屏2021-01-26 上午11.15.09.png

简单的来说@executable_path就是我们App所在路径,进入我们xctest的文件,显示包内容,那试一下,将YYModel拷贝进去试下。新建一个Frameworks文件夹,再将YYModel拷贝进来,再跑一下测试,成功。
截屏2021-01-26 上午11.36.22.png

当然我们不能这么干,拖进来的动态库签名和我们的App不一样,当然上架肯定过不了,而且这么干多麻烦。

注:系统在加载动态库时,会检查 framework 的签名,签名中必须包含 TeamIdentifier 并且 framework 和 host app 的 TeamIdentifier 必须一致。

其实这事情cocopod已经干过了,假如我们先创建XCTest的话,然后pod init的时候podfile里面会包含我们test的target,然后我们pod install的时候cocopod会自动帮我们做完我们需要干的事。
如果我们需要手动干这事的话,直接在pod的文件夹里面找到shell脚本,稍加修改,然后在工程的Build Phases里面添加一下run script就行了。
要改的地方就是下图这个地方


shell.png

2、静态库链接静态库

同样创建一个静态库,然后pod我们的YYModel,但是这次把use_frameworks!注释掉,那我们pod进来的就是静态库。依旧一样的方式,跑一下test,不出意外,报错Undefined symbol: _OBJC_CLASS_$_YYClassInfo找不到对应的文件。因为我们的XCTest(App)现在只能找到头文件。
那么还缺名称和位置,
缺名称:那么就在Other Link Flags里面告诉我们需要链接的库的名称。怎么写看一下上面连接器标志查找方式。这里我们用-l x这个标志

链接器标志-lx.png

缺位置:那么就在Library Search Path里面告诉我们需要链接的库的位置。然后测试,成功。
位置配置.png

为啥跟动态库不一样,直接成功了捏。因为上面说了,静态库会被完整地复制到可执行文件中,所以告诉编译器库位置之后就可以直接找到编译成功。

3、动态库链接静态库

动态库可以粗暴的约等于可执行文件,也就是说动态库生成的时候就已经将静态库链接进来了,因此就可以直接成功。

4、动态库链接动态库

创建一个动态库。跟上面一样的操作,pod,创建XCTest,测试,结果跟关联静态库关联动态库一样一样。那么解决方法当然也一样咯


截屏2021-01-27 下午4.42.13.png

个人理解,有问题请指正

你可能感兴趣的:(iOS组件化之动态库和静态库)