iOS中的动态库和静态库分析

2021-02-03 16:57:34

参考地址 初识iOS中的动态库和静态库

由于最近研究组件化后调试时二进制映射源码的功能,发现需要对开发中的动态库和静态库需要有一些了解。所以就有了这篇文章,由于只是了解,并没有深入到编译层面,所以本篇文章只是简单了解一些库的知识,并不深入。

静态库 vs 动态库

平常开发的时候会接触一些库的文件,比如.a.tbd.framework结尾的文件,甚至以前还有.dylib结尾的。由于以前一直没忙于工作,所以也没有细致的研究这些文件结尾的区别。只是傻傻的以为.a结尾的是静态库,.framework结尾的是动态库。现在看来以前是真的傻。现在来看一下静态库和动态库的区别。

静态库

如果我们的app依赖一个静态库,在链接阶段会把依赖静态库的部分合并到app的可执行文件中(app的可执行文件指的是app包内容里面同名的一个文件)。静态库的文件名后缀是.a.framework

动态库

编译app时并不会把动态库合并到app的可执行文件中,而是在程序启动的时候加载动态库到内存中。其中动态库分动态链接库动态加载库两种。

  • 动态链接库:在编译阶段需要指定app需要依赖哪些动态库。当运行可执行文件时,如果系统还没有加载过这些库时,就会随着运行可执行文件的加载一起加载这些动态库。如果有多个可执行文件依赖同一个动态库,不会重复加载。

  • 动态加载库:编译阶段不需要指定app需要依赖哪些动态库。当运行过程中需要加载某个动态库时,就会用dlopen函数动态的把库加载到内存中使用。

所以这两个的区别就是动态链接库在app运行时就需要加载,动态加载库可以在任何时间进行加载。动态库的文件后缀是.tbd.framework

小总结

由于静态库在链接时需要链接到可执行文件中,所以会导致包体积变大。动态库因为不会添加到可执行文件中,所以相比静态库包体积会小一些。

相对于启动时间,静态库会比动态库好一些,因为如果使用很多动态库,app在冷启动时加载动态库会占用一些时间。

下面来说一下文件后缀的问题。.a就是静态库,.tbd是动态库,以前还有.dylib结尾的动态库。.framework既可以是动态库也可以是静态库,这取决于制作静态库时我们选择的Mach-O type。现在无论是静态库还是动态库都推荐使用.framework。接下来会展示如何制作自己的.framework类型的动态库和静态库。

如何制作使用动态库和动态库

制作.a静态库

-w728创建工程的时候选择Static Library选项。然后随便起个名字就好。创建完工程应该是下面这样的:-w270然后写一个测试代码:

// CreateStatic.h#import <Foundation/Foundation.h>@interface CreateStatic : NSObject- (void)test;@end// CreateStatic.m#import "CreateStatic.h"@implementation CreateStatic- (void)test {
	NSLog(@"test static .a");}@end

然后对工程文件做一些配置:-w685设置Build Active Architecture OnlyNO,当设置为YES时,只会针对当前架构进行编译,当设置为NO时,会对所有架构进行编译。接下来编译一下就可以在Product文件夹下生成一个.a的静态库。 测试代码如下:

// CreateFrameworkStatic.h#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface CreateFrameworkStatic : NSObject- (void)test;@endNS_ASSUME_NONNULL_END// CreateFrameworkStatic.m#import "CreateFrameworkStatic.h"@implementation CreateFrameworkStatic- (void)test {
	NSLog(@"test create framework static library");}@end

-w409通过设置是否为Debug状态和是否为真机,会生成4个.a文件。可以用lipo -info来查看.a文件信息。-w658

接下来可以通过lipo -crate命令把这几个静态库合并成一个:-w1319可以看到合并后的.a文件既有真机又有模拟器的架构。

接下来创建一个工程,测试一下这个静态库:-w1665可以看到静态库可以用了。

制作Framework静态库

-w730

制作Framework静态库选择这个选项,创建工程。然后需要修改Build Setting选项,除了调整Build Active Architecture OnlyNO,还需要调整Mach-O TypeStatic Library-w673然后调整编译选项,会和原来创建.a静态库一样,生成四个Framework。这里偷个懒,只编译Debug+真机和Debug+模拟器选项。生成的文件目录如下:-w612其中红色框框的这个文件就是二进制文件,同样可以使用lipo -infolipo -create查看和合并库文件。-w1827

接下来测试一下Framework类型的静态库使用,当加到工程后,如果报framework not found的错误,需要调整Build SettingLibrary Search Path选项:-w1659

制作Framework动态库

制作Framework动态库和Framework静态库一样,创建Framework类型的工程,然后Mach-O Type选择Dynamic Library。 测试代码如下:

// CreateFrameworkDynamicLibrary.h#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface CreateFrameworkDynamicLibrary : NSObject- (void)test;@endNS_ASSUME_NONNULL_END// CreateFrameworkDynamicLibrary.m#import "CreateFrameworkDynamicLibrary.h"@implementation CreateFrameworkDynamicLibrary- (void)test {
	NSLog(@"test framework dynamic library");}@end

这里也只编译Debug+真机和Debug+模拟器两种:-w617其中上图红色框框就是动态库二进制文件,同样可以使用lipo -infolipo -create查看和合并库文件。-w1917接下来测试一下动态库使用,同样直接拖到项目里面就可以:-w1657

记得在Frameworks, Libraries, and Embedded Content中选择Embed & Sign这个选项,否则会报dyld: Library not loaded的错误。

我们上面添加动态库的方式会在程序启动的时候加载,如果项目在编译阶段没有直接使用这个动态库,可以设置在运行时加载动态库。需要把Build Phases里面Link Binary With Libraries里面的动态库删除,只保留Embed Frameworks-w1386

这样程序启动的时候就不会加载这个动态库,然后我们在页面里面添加一个按钮,点击按钮时加载动态库并执行动态库的方法:

- (IBAction)testDynamicLibrary:(id)sender {
	NSString *path = [NSString stringWithFormat:@"%@/%@", [NSBundle mainBundle].privateFrameworksPath, @"CreateFrameworkDynamicLibrary.framework"];
	[self loadDynamicLibraryWithPath:path];
	Class cl = NSClassFromString(@"CreateFrameworkDynamicLibrary");
	if (!cl) {
		NSLog(@"class not found");
	}
	[[cl new] performSelector:@selector(test)];}- (BOOL)loadDynamicLibraryWithPath:(NSString *)path {
	NSBundle *bundle = [NSBundle bundleWithPath:path];
	if (!bundle) {
		NSLog(@"%@ not found", path);
		return NO;
	}

	NSError *error;
	if (![bundle loadAndReturnError:&error]) {
		NSLog(@"Load %@ failed: %@", path, error);
		return NO;
	} else {
		NSLog(@"Load %@ success", path);
	}

	return YES;}

这样也可以成功加载动态库,这样加载动态库更像是一个动态加载库。

总结

  • 动态库和静态库其实都是一个编译产物,只是加载到app的方式不同。静态库在链接阶段参与的加载。而动态库是在app启动或app运行时加载。

  • 至于文件后缀就很好理解了,静态库可以是.a文件也可以是.framework文件。动态库现在一般都是.framework,不过.framework不一定都是动态库。

参考


  • 2019-03-15 15:33:08

    Xshell不能按退格、删除键的解决方案

    在使用xshell时,由于每个服务器不同,一些无法使用Backspace键向后删除字符。针对这个问题,本文为大家解答下退格键无法识别如何设置?

  • 2019-03-15 15:49:28

    win10远程桌面连接不上解决方法

    有朋友就感叹电脑的世界真的是很神奇,可以将整个世界连接在一起。如果别人想要摆弄你的电脑,即使不在一个地方也可以利用远程桌面来控制。而这就是所谓的远程控制操作了,大部分人都知道它的作用,不过这也不排除会遇到一些突发情况的时候,例如win10远程桌面连接不上,这该怎么去解决呢?为此,小编给大家带来了解决的图文操作。

  • 2019-03-15 16:49:18

    Win7无法进入家庭组提示“您的系统管理员不允许访问家庭组”怎么办

     家庭组是家庭网络上可以共享文件和打印机的一组计算机,可以方便用户们共享文件或者视频等,可是最近有win7纯净版系统用户却发现无法进入家庭组,提示“您的系统管理员不允许访问家庭组”,该怎么办呢?现在给大家分享一下Win7无法进入家庭组提示“您的系统管理员不允许访问家庭组”的解决方法。

  • 2019-03-17 22:19:28

    动态更新Toolbar Menu以及Menu中同时显示文字和图标

    我们经常会有这样的需求,在切换Fragment或者点击某个按钮后动态更新Toolbar上Menu项.但是onCreateOptionsMenu方法只在创建Activity的时候调用一次,以后就不再调用了,所以就不能在onCreateOptionsMenu中做处理了。 不过系统提供了另外的一个方法onPrepa

  • 2019-03-26 19:25:01

    Android studio 打包后安装闪退 Fatal Signal 6(SIGABRT)...

    项目上线前打包安装后闪退,查了很多原因,比如混淆文件的内容,第三方库不加入混淆等等,均未成功,后来关闭混淆打包后运行成功,原因可能是依赖工程中的库文件不能被混淆,关闭本工程混淆开关后,依赖工程的混淆开关也要关闭,关闭混淆后如果怕被反编译,可使用百度开发平台的app加固,加固的同时还能使用多渠道打包工具。

  • 2019-03-26 19:29:05

    Android NDK开发Crash错误定位

     在Android开发中,程序Crash分三种情况:未捕获的异常、ANR(Application Not Responding)和闪退(NDK引发错误)。其中未捕获的异常根据logcat打印的堆栈信息很容易定位错误。ANR错误也好查,Android规定,应用与用户进行交互时,如果5秒内没有响应用户的操作,则会引发ANR错误,并弹出一个系统提示框,让用户选择继续等待或立即关闭程序。并会在/data/anr目录下生成一个traces.txt文件,记录系统产生anr异常的堆栈和线程信息。如果是闪退,这问题比较难查, --------------------- 作者:xyang0917 来源:CSDN 原文:https://blog.csdn.net/xyang81/article/details/42319789 版权声明:本文为博主原创文章,转载请附上博文链接!

  • 2019-04-01 22:46:39

    电子签章的实施方案

    WORD/EXCEL签章模块,该部分实现与WORD/EXCEL的无缝结合,并提供给用户简单直观的菜单和工具条来实现文档签章验证等各种操作,其中,KHSC-64智能密码钥匙是签章模块中用户证书和图章的载体