[iOS] 静态库和动态库的理解

前言:之前对于静态库和动态库的了解太过于片面,网上的文章大都是大佬们的理解,这里重新梳理一下,方便日后翻阅。

1. 库

库(Library)其实就是一段编译好的二进制代码,可以被操作系统载入内存执行。

1.1 应用场景

  • 需要将代码给别人使用,但不想别人看到源码,就需要将代码打包成库,只暴露出头文件(比如经常使用的一些三方库)
  • 对于不会进行大改动的模块,可以打包成库,可以减少编译的时间,库已经是编译好的二进制了,编译的时候只需要Link 一下,不会浪费编译时间。

1.2 概念介绍

上面在应用场景中提到了Link(链接),Link 又区分为静态链接动态链接,所以便有了静态库动态库的概念。

1.2.1 framework

framework 其实是一种打包方式,它既可以是动态库也可以是静态库(系统提供的framework 是动态库,我们自己做的一般是静态的,苹果不允许我们使用自己做的动态库)。它只是将二进制文件、头文件和资源文件打包到了一起,方便管理和分发,和静态库&动态库的本质没有什么关系

1.2.2 静态库

静态库(静态链接库)文件以.a或者.framework结尾,之所以叫静态库,是因为在编译的时候它会被复制一份到可执行文件中,多次使用就有多份拷贝,程序运行时将不再需要改静态库。

  • 优点:
    目标程序没有外部依赖,直接就可以运行;

  • 缺点:
    会增大目标程序的体积;

.a 和 .framework有什么区别?
.a是一个纯二进制文件,.framework中除了有二进制文件之外还有资源文件;
.a文件不能直接使用,至少要有.h文件配合,.framework文件可以直接使用;
.a + 头文件 + 资源文件 = .framework。

1.2.3 动态库

动态库(动态链接库) 以.dylib 或者.framework后缀结尾,它和静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。程序运行时,动态库才会被真正加载进来。

  • 优点:
    不影响目标程序体积;
    可以被多个程序共享,多个程序都可以动态链接到同一个动态库;

  • 缺点:
    运行时才使用,影响启动速度(APP 启动时会链接动态库,数量多了之后耗费时间);
    程序依赖于外部环境,如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行;

1.2.4 iOS系统下的framework

iOS系统下的 framework 可以分为 3 类:

    1. 系统级别的动态库:Dynamic Framework
      Dynamic Framework,动态库,系统提供的framework 都是动态库,比如 UIKit.framework,具有所有动态库的特性:在程序运行时由系统加载到内存,系统只加载一次,比如抖音和微信的可执行文件加载动态库时,内存中只有一份动态库 A:
      image.png
    1. 用户级别的动态库:Embedded Framework
      Embedded Framework,这个是用户可以制作的“动态库”,它是受到 iOS 平台限制(签名机制和沙盒机制限制)的动态库,它具有部分动态特性,比如:Embedded Framework 可以在Extension可执行文件 和APP可执行文件 之间共享,但是不能像系统的动态库一样,在不同的 APP(进程) 中共享。
      Embedded Framework最后也还是要拷贝到应用程序中,但是与静态库不同,它不在可执行文件中
      image.png

本质上讲,Embedded Framework 是动态库,只是我们给动态库起的一个别名!

    1. 用户级别的静态库:Static Framework
      Static Framework,静态库,用户可以制作,可以粗略的理解为,它等价于 头文件 + 资源文件 + 二进制代码,它具有静态库的属性。

如下图,抖音和微信的可执行文件中都会有这个静态库A:


image.png

2. 创建 Framework

下图是Xcode12 提供的创建 framework 的模板:

截屏2021-02-28 下午4.08.25.png

我们可以使用者两个模块,分别创建一个动态的framework 和一个静态的 framework

2.1 制作静态库

2.1.1 制作.a静态库

我们需要创建一个工程,选择为Static Library 模板,工程项目名为:TestStaticLibrary,如下图:

截屏2021-02-28 下午4.38.18.png

我们可以看到项目中有一个和项目同名的类,Product 下有一个红色的.a文件,说它目前是不存在的,如下:

image.png

在和项目同名类TestStaticLibrary里加上一个实例方法,如下:

@interface TestStaticLibrary : NSObject

- (void)log;

@end

@implementation TestStaticLibrary

- (void)log{
    NSLog(@"Hello TestStaticLibrary!!!");
}

@end

我们在模拟器下编译一下这个.a文件,可以看到.a文件在Debug-iphonesimulator目录下:

截屏2021-02-28 下午4.41.44.png

我们再新建一个工程:TestApp,然后把上面生成的.a文件添加到TestApp项目中,同时也需要添加.a的头文件TestStaticLibrary.h,然后就可以使用了:

image.png

2.1.2 制作 framework 静态库

同样需要创建一个工程,选择为 Framework 模板,工程项目名为:TestStaticFramework,如下图:

image.png

可以看到 Products下刚好就是.framework了,并且有一个和项目同名的头文件TestStaticFramework.h,如下图:

image.png

创建的其实默认是动态库,我们需要修改一下:Build Settings => Mach-O Type 改为Static Library,如下图:

截屏2021-02-28 下午5.17.41.png

关于 Mach-O文件的理解,可以查看Mach-O文件介绍这篇文章。

我们创建一个Student类,里面也只有一个实例方法:

@interface Student : NSObject

- (void)log;

@end

@implementation Student

- (void)log{
    NSLog(@"Hello TestStaticFramework Student!!!");
}

@end

我们在TestStaticFramework.h头文件中引入刚创建的Student.h头文件,并且在下面这个配置里将 Student.h文件,从 Project中拖入到Public下面暴露出来,如下:

image.png

公开的头文件中,#import的其他类也要公开,添加到public中,如果不想公开,就在头文件用用@class的方式,在对应的.m中用#import方式。

然后选择模拟器进行编译,之后将Products下的TestStaticFramework.framework 添加到上面创建的TestApp项目中,导入头文件直接使用就可以了,如下图:

image.png

静态库的使用比较简单,直接将framework 添加到项目中即可。

2.2 制作 Embedded Framework

我们只能制作.framework这样的动态库,制作过程和上面.framework静态库的过程一样,只不过系统默认设置的就是动态类型(Mach-O Type 默认为Dynamic Library),这次就不需要修改了。

我们创建一个 Teacher类,里面也有一个log 方法,之后暴露头文件的方法和上面.framework静态库一样,这里就不多说了。

我们将在模拟器下编译的好的 TestDynamicFramework.framework 文件导入 TestApp 项目中,编译之后,没问题。

现在,我们引入头文件,调用 Teacher的实例方法,运行之后,就崩溃了,如下图:

image.png

解决方案:
我们需要在项目TargetGeneral -> Frameworks,Libraries, and Embedded Content中将TestDynamicFramework.frameworkDo Not Embed 修改为Embed & Sign 之后才能正常使用,如下图:

截屏2021-02-28 下午7.36.04.png

Embed:嵌入,用于动态库,动态库在运行时链接,所以它们需要被打进.app里面。
Signing:签名,用于动态库,如果已经有签名了就不需要再签名,codesign -dv framework链接 ,如果返回code object is not signed at all 或者 adhoc,选择Embed&Sign,否则选择 Embed Without Signing

3. 动态库和静态库的差异

3.1 Xcode 配置

使用静态库的时候直接将.framework导入就可以使用了。
在使用动态库的时候,需要对.framework进行Embed&Singing设置。

3.2 在 .app 包中的表现

经过上面的步骤,我们的 TestApp 项目已经引入了libTestStaticLibrary.a静态库 TestStaticFramework.framework静态库 和 TestDynamicFramework.framework动态库,现在我们在TestApp.app 文件中查看下有什么不同:

截屏2021-02-28 下午7.47.31.png

可以看到TestDynamicFramework.framework动态库被复制到了.app文件中的Frameworks 文件夹下,另外我们用 MachOView工具打开TestApp可执行文件,在 Load Commands 下可以看到有LC_LOAD_DYLIB(TestDynamicFramework.framework) ,如下图:

image.png

从上面图中我们看到,TestDynamicFramework.framework动态库存放在.app中了,那另外两个静态库呢?
静态库在编译的时候其实就被链接到TestApp.app可执行文件中了,如下图:

截屏2021-02-28 下午9.47.15.png

4. 不同架构的库

上面编译静态库或者动态库都是在模拟器下编译的,它只支持当前选择设备的架构,我们可以使用 libo -info 命令查看一下,以.framework静态库为例,如下,只支持x86_64

demo@Pro ~ % lipo -info TestStaticFramework.framework/TestStaticFramework
Non-fat file: TestStaticFramework.framework/TestStaticFramework is architecture: x86_64

这样的静态库如果用于真机的话,肯定会报错的,所以我们需要生成支持模拟器和真机的静态库,首先需要修改配置:Build Active Architecture Only改为NO:

截屏2021-02-28 下午10.10.09.png

修改TestStaticFrameworkBuild 配置为release,如下图:

截屏2021-02-28 下午10.12.36.png

选择Generic iOS Device任意一个模拟器各编译一次,然后Show in Finder,如下图:

截屏2021-02-28 下午10.14.25.png

可以看到真机和模拟器都生成了.framwork,分别在Release-iphoneos下和Release-iphonesimulator下,可以用命令lipo -info 二进制文件,看下支持的iOSCPU架构

image.png

  • Release-iphoneos里面支持armv7、arm64,属于真机,用到模拟器就会报错
  • Release-iphonesimulator里面支持i386、x86_64,属于模拟器,用到真机会报错

我们肯定不能用两个静态库啊,我们可以使用lipo 命令合并这两个静态库,如下:

lipo -create 二进制文件1 二进制文件2 -output 最终的可执行文件路径

将合成的二进制文件替换掉原来的,然后把这个新的framework拖进项目就可以使用了。

合成这一步可以使用脚本来完成,另外,如果静态库中有Category类,就要在使用静态库项目的配置中添加-ObjC或者-all_load

目前先记录这么多,等再遇到相关问题,持续更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容