楼主: z74646
1010 0

[其他] 关于静态库动态库的经验总结 [推广有奖]

  • 0关注
  • 1粉丝

本科生

75%

还不是VIP/贵宾

-

威望
0
论坛币
300 个
通用积分
0
学术水平
0 点
热心指数
1 点
信用等级
0 点
经验
890 点
帖子
55
精华
0
在线时间
12 小时
注册时间
2014-2-26
最后登录
2016-7-2

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

最近整理一些项目以前依赖留下的问题,在使用 CocoaPods 和 Carthage 的时候引出了关于静态库和动态库的思考,手动编译静态库的朋友应该知道,如果自己编译了一个静态库,这个静态库依赖了 iOS 自带的库,即使你在 Xcode开发工具http://www.maiziedu.com/course/234/中显式指定了依赖,当你在另一个工程中使用这个静态库的时候,依旧需要显式在那个工程指定这个静态库所依赖的系统库。

举个栗子

AFNetworking 是个好东西,大家都喜欢用,在日常使用的时候,一般都是使用 CocoaPods 引入这个库,笔者前面有篇文章分析了 CocoaPods 做了什么工作,但是却没有分析 CocoaPods 为何如此设计,这里先卖个关子不讲。AFNetworking 依赖Security.framework MobileCoreServices.frameworkSystemConfiguration.framework 三个系统框架,如果我们将其编译为静态库,然后这个静态库被其他工程依赖,那么其他工程依旧需要引入这三个框架。如果我们将其编译为动态库,则不需要依赖这三个框架。这个情况引起了笔者的兴趣

分析

既然问题是编译链接,那么就需要从动态库和静态库入手分析,首先,我们需要明确的是,系统提供的 framework 都是动态库的形式提供的。这很好理解,因为 UIKit 这类 framework 被使用的太频繁了,内存中完全只需要保留一份副本。这样也能减轻 App 的大小。那么直接来着手模拟一下其编译过程。

模拟静态库的编译

创建一个动态库用于模拟系统库

dynamic.h

----------void hello();

dynamic.c

----------#include "dynamic.h"#include <stdio.h>

void hello() {

    printf("Hello World");

}

然后编译打包动态库

> gcc -c -o dynamic.o dynamic.c # 编译为对象文件

> gcc -shared dynamic.o -o libdynamic.so

创建一个静态库用于模拟第三方静态库

static.h

--------void sayHello();

static.c

--------#include "static.h"#include "dynamic.h"

void sayHello() {

    hello();

}

然后编译静态库

> gcc -c -o static.o static.c

> ar -r libstatic.a static.o

这里大家有没有发现一个问题,静态库实际上并没有链接动态库,仅仅只是 include 了一个头文件用于编译通过。最终生成的静态库根本不知道动态库的存在。然后创建一个带有main 函数的程序

hello.c

-------#include "static.h"int main(int argc, char *argv[]) {

    sayHello();

}

然后编译

> gcc -o hello hello.c -L. -lstatic# 直接报错没有找到 hello() 的二进制代码

Undefined symbols for architecture x86_64:

  "_hello", referenced from:

      _sayHello in libstatic.a(static.o)

ld: symbol(s) not found for architecture x86_64

clang: error: linker command failed with exit code 1 (use -v to see invocation)

模拟动态库的编译

创建一个动态库用于模拟系统库

dynamic1.h

----------void hello();

dynamic1.c

----------#include "dynamic1.h"#include <stdio.h>

void hello() {

    printf("Hello World");

}

然后编译打包动态库

> gcc -c -o dynamic1.o dynamic1.c # 编译为对象文件

> gcc -shared dynamic1.o -o libdynamic1.so

创建动态库用于模拟第三方动态库

dynamic2.h

--------void sayHello();

dynamic2.c

--------#include "dynamic2.h"#include "dynamic1.h"

void sayHello() {

    hello();

}

然后编译打包动态库

> gcc -c -o dynamic2.o dynamic2.c # 编译为对象文件

> gcc -shared dynamic2.o -o libdynamic2.so# 报错,显示 dynamic2.o 中没有 hello() 二进制代码

Undefined symbols for architecture x86_64:

  "_hello", referenced from:

      _sayHello in dynamic2.o

ld: symbol(s) not found for architecture x86_64

clang: error: linker command failed with exit code 1 (use -v to see invocation)

> gcc -shared dynamic2.o -o libdynamic2.so -L. -ldynamic1# 成功,无报错

然后创建一个带有 main 函数的程序

hello.c

-------#include "static.h"int main(int argc, char *argv[]) {

    sayHello();

}

然后编译

> gcc -o hello hello.c -L. -ldynamic2

总结

从上面两个编译过程大家应该也能明白了,静态库实际上只是对象文件的打包,也就是说,只经过了编译过程,而没有链接过程,编译一个静态库甚至只需要满足所有函数的声明就行,而动态库虽然没有经过正规的链接,但是实际上还是通过 gcc 做了跟其他动态库的链接,动态库自身有了依赖的概念。所以不需要在工程中显式依赖了。这里可能有朋友想要问,这种知识有什么用处?实际上这种知识在依赖管理中有用,前面说过 CocoaPods 对工程修改了很多内容,这是有原因的,因为 iOS8 之前是不存在动态库的,只存在静态库,CocoaPods 不得不对目标工程也作出修改来添加对其他库的依赖,因为静态库不知道自身依赖什么库。而 Carthage 则只支持 iOS8 和动态库,所以完全可以没有侵入性,只需要提供一个动态库,然后工程依赖这个动态库就行了。

原文来自:推酷


二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:经验总结 Architecture Hello World Networking Frameworks 动态

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2024-4-20 04:08