动态库和静态库的区别

一、基本概念

静态库(Static Library)

  • iOS/macOS : .a 文件
  • Windows : .lib 文件
  • Linux : .a 文件

动态库(Dynamic Library)

  • iOS/macOS : .dylib.framework
  • Windows : .dll 文件
  • Linux : .so 文件

二、核心区别对比表

对比维度 静态库 动态库
链接时机 编译时链接到可执行文件 运行时加载
文件大小 增加可执行文件体积 不增加可执行文件体积
内存占用 每个进程独立占用内存 多进程共享内存
更新方式 需要重新编译整个程序 只需替换库文件
加载速度 启动快(已在可执行文件中) 启动慢(需要动态加载)
依赖管理 无运行时依赖 需要确保库文件存在
版本冲突 不存在 可能存在版本冲突
代码复用 每个程序独立拷贝 多个程序共享一份

三、工作原理图解

静态库链接过程

flowchart LR subgraph 编译时 A[源代码 main.c] --> B[编译器] C[静态库 libmath.a] --> B B --> D[链接器] D --> E[可执行文件 app
包含库代码] end subgraph 运行时 E --> F[直接运行
无需外部依赖] end style E fill:#FF6B6B style F fill:#98FB98

动态库链接过程

flowchart LR subgraph 编译时 A[源代码 main.c] --> B[编译器] C[动态库 libmath.dylib] -.引用.-> B B --> D[链接器] D --> E[可执行文件 app
只包含引用] end subgraph 运行时 E --> F[动态加载器] G[libmath.dylib] --> F F --> H[程序运行] end style E fill:#FFD93D style G fill:#FFA07A style H fill:#98FB98

四、内存占用对比

静态库内存模型

graph TB subgraph 内存 A[进程 A
包含 libmath 代码] B[进程 B
包含 libmath 代码] C[进程 C
包含 libmath 代码] end style A fill:#FF6B6B style B fill:#FF6B6B style C fill:#FF6B6B

特点: 每个进程都有一份完整的库代码副本

动态库内存模型

graph TB subgraph 内存 D[共享库区域
libmath.dylib
只加载一次] A[进程 A] -.引用.-> D B[进程 B] -.引用.-> D C[进程 C] -.引用.-> D end style D fill:#98FB98 style A fill:#FFD93D style B fill:#FFD93D style C fill:#FFD93D

特点: 多个进程共享同一份库代码,节省内存


五、详细对比

1. 文件大小对比

静态库示例

bash 复制代码
# 编译静态库
gcc -c math.c -o math.o
ar rcs libmath.a math.o

# 链接到程序
gcc main.c -L. -lmath -o app_static

# 查看大小
ls -lh app_static
# 输出: -rwxr-xr-x  1 user  staff   50K  app_static  ← 包含库代码

动态库示例

bash 复制代码
# 编译动态库
gcc -shared -fPIC math.c -o libmath.dylib

# 链接到程序
gcc main.c -L. -lmath -o app_dynamic

# 查看大小
ls -lh app_dynamic
# 输出: -rwxr-xr-x  1 user  staff   10K  app_dynamic  ← 只包含引用

ls -lh libmath.dylib
# 输出: -rwxr-xr-x  1 user  staff   20K  libmath.dylib

对比:

  • 静态链接: app_static = 50K(包含所有代码)
  • 动态链接: app_dynamic (10K) + libmath.dylib (20K) = 30K

2. 更新方式对比

静态库更新流程

flowchart TD A[库代码有 Bug] --> B[修改库源代码] B --> C[重新编译静态库] C --> D[重新编译所有使用该库的程序] D --> E[重新发布所有程序] style A fill:#FF6B6B style E fill:#FFD93D

动态库更新流程

flowchart TD A[库代码有 Bug] --> B[修改库源代码] B --> C[重新编译动态库] C --> D[替换动态库文件] D --> E[程序自动使用新库] style A fill:#FF6B6B style E fill:#98FB98

3. iOS 开发中的实际应用

静态库 (.a)

objective-c 复制代码
// 创建静态库项目
// File -> New -> Project -> Cocoa Touch Static Library

// MyStaticLib.h
#import <Foundation/Foundation.h>

@interface MyStaticLib : NSObject
+ (NSString *)getVersion;
@end

// MyStaticLib.m
@implementation MyStaticLib
+ (NSString *)getVersion {
    return @"1.0.0";
}
@end

// 使用静态库
// 1. 将 libMyStaticLib.a 添加到项目
// 2. 在 Build Phases -> Link Binary With Libraries 中添加
// 3. 直接使用
#import "MyStaticLib.h"
NSString *version = [MyStaticLib getVersion];

特点:

  • ✅ 编译后代码在 App 内部,无需额外文件
  • ✅ 启动速度快
  • ❌ 增加 App 体积
  • ❌ 更新需要重新提交 App

动态库 (.framework)

objective-c 复制代码
// 创建动态库项目
// File -> New -> Project -> Cocoa Touch Framework

// MyDynamicLib.h
#import <Foundation/Foundation.h>

@interface MyDynamicLib : NSObject
+ (NSString *)getVersion;
@end

// MyDynamicLib.m
@implementation MyDynamicLib
+ (NSString *)getVersion {
    return @"2.0.0";
}
@end

// 使用动态库
// 1. 将 MyDynamicLib.framework 添加到项目
// 2. 在 General -> Frameworks, Libraries, and Embedded Content
//    选择 "Embed & Sign"
// 3. 使用
#import <MyDynamicLib/MyDynamicLib.h>
NSString *version = [MyDynamicLib getVersion];

特点:

  • ✅ 不增加主 App 体积
  • ✅ 可以在 App Extension 间共享
  • ✅ 支持热更新(越狱环境)
  • ❌ 启动速度稍慢
  • ❌ 需要确保 framework 正确嵌入

六、性能对比

启动时间对比

gantt title 应用启动时间对比 dateFormat X axisFormat %L ms section 静态库 加载可执行文件 :0, 50 初始化 :50, 100 section 动态库 加载可执行文件 :0, 30 加载动态库 :30, 80 符号解析 :80, 120 初始化 :120, 170

结论: 静态库启动更快(100ms vs 170ms)


七、实际案例分析

案例 1: 系统库(动态库)

objective-c 复制代码
// iOS 系统库都是动态库
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

// 这些库在系统中只有一份
// 所有 App 共享,节省内存

案例 2: 第三方库

CocoaPods 静态库配置

ruby 复制代码
# Podfile
platform :ios, '13.0'
use_frameworks! :linkage => :static  # 使用静态库

target 'MyApp' do
  pod 'AFNetworking'
  pod 'SDWebImage'
end

CocoaPods 动态库配置

ruby 复制代码
# Podfile
platform :ios, '13.0'
use_frameworks!  # 使用动态库(默认)

target 'MyApp' do
  pod 'AFNetworking'
  pod 'SDWebImage'
end

八、选择建议

使用静态库的场景

mindmap root((静态库)) 性能要求高 启动速度关键 实时性应用 独立分发 单一可执行文件 无外部依赖 安全性 代码不易被替换 防止篡改 小型项目 库数量少 体积可控

使用动态库的场景

mindmap root((动态库)) 代码共享 多个 App 使用 App Extension 频繁更新 独立更新库 不影响主程序 大型项目 模块化开发 减少主程序体积 插件系统 动态加载 可选功能

九、iOS 特殊情况

iOS 动态库限制

flowchart TD A[iOS 动态库] --> B{库来源} B -->|系统库| C[允许
UIKit, Foundation 等] B -->|自定义库| D{使用场景} D -->|主 App 内部| E[允许
Embed & Sign] D -->|App Extension| F[允许
共享 Framework] D -->|跨 App 共享| G[ 不允许
沙盒限制] style C fill:#98FB98 style E fill:#98FB98 style F fill:#98FB98 style G fill:#FF6B6B

App Extension 共享 Framework

scss 复制代码
MyApp.app/
├── MyApp (可执行文件)
├── Frameworks/
│   └── SharedLib.framework  ← 主 App 和 Extension 共享
└── PlugIns/
    └── MyExtension.appex/
        └── MyExtension (可执行文件)

配置方法:

objective-c 复制代码
// 1. 创建 Framework (Dynamic)
// 2. 在主 App Target 中: Embed & Sign
// 3. 在 Extension Target 中: Do Not Embed
// 4. 两者都可以使用该 Framework

十、常见问题

Q1: 为什么 iOS 主要使用动态库?

markdown 复制代码
原因:
1. 系统库共享,节省内存
2. 支持 App Extension 代码共享
3. 模块化开发,便于维护
4. Swift 库必须是动态库(Swift ABI 稳定前)

Q2: 静态库会增加多少体积?

bash 复制代码
# 示例计算
静态库大小: 5MB
使用该库的函数: 20%

实际增加体积:
- 理论上: 5MB (完整拷贝)
- 实际上: 1-2MB (链接器会移除未使用代码)

Q3: 如何查看 App 使用的库?

bash 复制代码
# 查看动态库依赖
otool -L MyApp.app/MyApp

# 输出示例:
/System/Library/Frameworks/UIKit.framework/UIKit
/System/Library/Frameworks/Foundation.framework/Foundation
@rpath/MyDynamicLib.framework/MyDynamicLib  ← 自定义动态库

十一、最佳实践

混合使用策略

graph TB A[第三方库选择] --> B{库特征} B -->|基础工具库
如 JSON 解析| C[静态库
减少启动时间] B -->|UI 组件库
频繁更新| D[动态库
便于维护] B -->|大型 SDK
如地图| E[动态库
减少体积] B -->|核心业务库
稳定| F[静态库
提高性能] style C fill:#FFD93D style D fill:#FFA07A style E fill:#FFA07A style F fill:#FFD93D

性能优化建议

objective-c 复制代码
// 1. 减少动态库数量
// 不推荐: 10 个小动态库
// 推荐: 合并为 2-3 个大动态库

// 2. 延迟加载非必要库
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    // 加载非启动必需的库
    [self loadOptionalLibraries];
});

// 3. 使用静态库优化启动速度
// 核心启动库 -> 静态库
// 其他功能库 -> 动态库

总结

维度 静态库 动态库 推荐场景
体积 增加 App 体积 不增加 动态库 ✅
性能 启动快 启动慢 静态库 ✅
更新 需重新编译 独立更新 动态库 ✅
共享 不支持 支持 动态库 ✅
依赖 无外部依赖 需确保库存在 静态库 ✅

一般建议:

  • 🎯 小型 App: 优先使用静态库
  • 🎯 大型 App: 混合使用,核心用静态,功能用动态
  • 🎯 SDK 开发: 提供两种版本供开发者选择
相关推荐
WYiQIU5 小时前
突破字节前端2-1⾯试: JS异步编程问题应答范式及进阶(视频教学及完整源码笔记)
开发语言·前端·javascript·vue.js·笔记·面试·github
byc5 小时前
Android 存储目录<内部存储,外部存储app专属,外部存储公共>
android·面试
长安er5 小时前
LeetCode 11盛最多水的容器 & LeetCode 42接雨水-双指针2
面试·力扣·双指针·接雨水
a努力。5 小时前
网易Java面试被问:fail-safe和fail-fast
java·windows·后端·面试·架构
踏浪无痕6 小时前
MySQL 脏读、不可重复读、幻读?一张表+3个例子彻底讲清!
后端·面试·架构
豆苗学前端6 小时前
彻底讲透浏览器的事件循环,吊打面试官
前端·javascript·面试
Moe4886 小时前
Elasticsearch 8.1 Java API Client 客户端使用指南(索引、文档操作篇)
java·后端·面试
踏浪无痕6 小时前
彻底搞懂微服务 TraceId 传递:ThreadLocal、TTL 与全链路日志追踪实战
后端·微服务·面试
扣丁梦想家7 小时前
面试基础整理之 ArrayList
面试·职场和发展