iOS 静态库&动态库依赖问题

一、库简介

1.1 库的形式

iOS这边库可以分为静态库(Static Library)动态库(Dynamic Library)

  • 动态库 Framework
  • 静态库 Framework 和 .a 文件

1.2 库的区别

下文默认读者具有静态链接&动态链接相关知识,如有不明,请翻阅 mach-o分析 参考

相同点

两种库在构建的过程中,都需要经过编译,会将对应的 .m 文件编译成 .o 文件。这样也就造成了一个问题,任何编译配置宏等已经被展开后编译完成。也就是说,生成库时使用的 Debug 环境生成。即使将来被集成进项目中时,项目内配置 Release 环境出包,不会影响到库的逻辑。因为库中的代码是在生成是已经编译完成的,不随项目代码一起编译。

不同点

  1. 动态库在构建的过程中,需要经过静态链接。这里你没有看错,动态库的生成需要静态链接。而静态库的生成,不需要经过静态链接,仅仅只是简单的将对应的 .o 文件压缩。所以这里也可以通过命令行工具将 .o 文件重新解压缩出来。

    这里我们重点说一下动态库,动态库和我们项目产出的主工程可执行文件对比,其编译、链接等过程是完全一样的。换句话说,动态库是一个没有 main 函数的可执行文件。

  2. 在使用上,动态库是在程序启动运行时,被动态链接后执行调用的。而静态库则参与程序的静态链接,被链入主工程的二进制可执行文件中。这也就是为什么,动态库需要被拷贝内嵌 (embed) 到包内,静态库不需要的原因。

    同样,动态库 A 在被程序动态链接后,动态库 A 如果依赖动态库 B,也会动态链接 B。所以动态库 B 也需要被内嵌到包内,否则会报错“Library not loaded xxx”

1.3 总结

动态库和静态库,在生成时,因为其是否经过静态链接,产生了差异。动态库经过静态链接后,会经过符号决议、重定位等流程,会将依赖的静态库链接进来,也就是说,动态库会吸附静态库。如果依赖的是动态库,则走动态链接的流程。

在使用时,因为动态库只需要动态链接,所以不会在主工程编译阶段报错,但可能在运行阶段报找不到库。静态库则需要被主工程静态链接,所以当缺少符号或者符号重复冲突时,会在编译阶段报错。

这里注意区分库的生成时机、库被主工程引入编译的时机、主工程启动运行后的动态链接时机。

下面列出了库依赖及冲突的各种场景,尝试不看原因,看看是否能独立分析出,出现这种现象的原因。

二、库依赖场景下的符号冲突问题

2.1 两个静态库

2.1.1 两个静态库有相同符号:

  • 场景:静态库A、B均采用Framework的方式来创建,其中 A、B 包含同一个类 Obj ,然后将 A、B 同时集成到工程中去。
  • 结果:在链接(link)阶段报符号重复。
  • 原因:A、B均需要参与主工程的静态链接,会在静态链接的符号决议过程中,发送冲突。

2.1.2 静态库 A 依赖静态库 B:

  • 场景:静态库A、B均采用 Framework(.a类似) 的方式来创建,其中 A 依赖 B。A 库在 Framework Search Path 中正确设置 B 库路径(思考为什么可以不设置 Link Binary With Libraries)。A、B库代码如下:

    // 静态库A
    @interface ObjA : NSObject
    + (void)test;
    @end
    
    @implementation ObjA
    + (void)test {
        NSLog(@"ObjA Test");
        [ObjB test]; //依赖 B 库中的类方法
    }
    @end
      
    // 静态库B
    @interface ObjB : NSObject
    + (void)test;
    @end
    
    @implementation ObjB
    + (void)test {
        NSLog(@"ObjB Test");
    }
    @end
      
    // 主工程中调用 [ObjA test];
    
  • 结果:主工程中,如果我们 A 正常使用,B 仅设置Framework Search Path,让工程可以正确搜索到Framework,但是没有设置linker flag,或者没有设置 Link Binary With Libraries。则会在编译的时候报缺少符号。

  • 原因:A静态库生成过程,因为并没有经过静态链接,所以并不会包含 B 库的符号。A、B均需要参与主工程的静态链接,但此时B没有设置Link Binary With Libraries,所以会在静态链接的符号决议过程中,找不到对应的符号,报错。

  • 推广:这里如果是静态库 .a 依赖静态库 .framework、.a 依赖 .a也是一样的情况。

  • 注意:上述情况有一个例外:.framework 静态库 依赖 .a 静态库。在这种情况下,如果我们在 A 库中设置了Library Search Path 或 Link Binary With Libraries。会导致静态库的重新压缩,生成出来的 A 库会包含 B 库的.o文件。使用 A 库的时候,也就不再需要引入 .a 静态库B,否则会报符号冲突。如果不想 .a 静态库B被压缩进 .framework 静态库A,则.framework 静态库A仅仅将 .a库B的头文件引入即可,不需要设置Library Search Path 或 Link Binary With Libraries。因为A库生成时仅仅压缩,并没有静态链接,所以这样设置不会报错,只要让编译器可以正常校验通过即可。

2.2 两个动态库

2.2.1 两个动态库包含相同的符号:

  • 场景:动态库A、B,其中 A、B 包含同一个类 Obj ,然后将 A、B 同时集成到工程中去。主工程调用[Obj test];
  • 结果:运行无异常,启动时控制台会输出一个警告“Class Obj is implemented in both xxx and xxx”。大概意思就是 A.framework 和 B.framework 的可执行文件里面都包含了 Obj 这个类。至于选哪个,取决于linker flag,或者 Link Binary With Libraries 中的先后顺序,先被动态链接的会被调用到。
  • 原因:A、B均需要参与主工程的动态链接,仅会符号绑定(bind)一次,所以先绑定的会被调用到。

2.2.2 动态库 A 依赖动态库 B:

  • 场景:动态库A、B,其中代码同 2.1.2。A 库在 Framework Search Path 中正确设置 B 库路径、Link Binary With Libraries也需要设置(注意和2.1.2静态库情况的对比)。
  • 结果:主工程这里同样只正确引入A,注意动态库需要选 embed。B库不引,或者仅设置Framework Search Path。结果build success,但是程序启动就 crash ,控制台报错“Library not loaded xxx”
  • 原因:因为A 依赖 B,所以 B 也会被动态链接。以为编译时仅仅静态链接,所以编译可以正常通过。但是因为B库没有没内嵌,所以启动时动态链接,会报错,不能正确的加载B库。

2.3 一动一静

2.3.1 静态库和动态库包含相同符号:

  • 场景:静态库A、动态库B,其中 A、B 包含同一个类 Obj ,然后将 A、B 同时集成到工程中去。主工程调用[Obj test];
  • 结果:运行无异常,启动时控制台会输出一个警告“Class Obj is implemented in both xxx and xxx”。主工程中调用,会调入静态库A。动态库B的调用,会调B库自己内部的。
  • 原因:因为静态库会在主工程静态链接时,被正确的链接进二进制可执行文件。同样,动态库B也会在生成时将源码生成的 .o 文件正确的静态链接进去。所以最终各自调用各自的。

2.3.2 静态库依赖动态库:

  • 场景及结论:静态库A、动态库B,A依赖B。生成A的时候,只需设置 Framework Search Path 即可,因为生成A不需要静态链接,仅仅只是压缩 .o 文件,只需要编译器不报错即可。主工程使用时,需要将 A、B都引入。原因不在赘述,读者自行思考。

2.3.3 动态库依赖静态库:

  • 场景及结论:动态库A、静态库B,A依赖B。生成A的时候,不仅要设置 Framework Search Path ,还需要设置 Link Binary With Libraries ,因为A需要静态链接,且会将静态库B吸附。主工程使用时,只需要将 A引入即可。原因不在赘述,读者自行思考。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容