动态库和静态库的区别

41 阅读4分钟

一、基本概念

静态库(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<br/>包含库代码]
    end
    
    subgraph 运行时
        E --> F[直接运行<br/>无需外部依赖]
    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<br/>只包含引用]
    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<br/>包含 libmath 代码]
        B[进程 B<br/>包含 libmath 代码]
        C[进程 C<br/>包含 libmath 代码]
    end
    
    style A fill:#FF6B6B
    style B fill:#FF6B6B
    style C fill:#FF6B6B

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

动态库内存模型

graph TB
    subgraph 内存
        D[共享库区域<br/>libmath.dylib<br/>只加载一次]
        
        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. 文件大小对比

静态库示例

# 编译静态库
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  ← 包含库代码

动态库示例

# 编译动态库
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)

// 创建静态库项目
// 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)

// 创建动态库项目
// 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: 系统库(动态库)

// iOS 系统库都是动态库
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

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

案例 2: 第三方库

CocoaPods 静态库配置

# Podfile
platform :ios, '13.0'
use_frameworks! :linkage => :static  # 使用静态库

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

CocoaPods 动态库配置

# 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[允许<br/>UIKit, Foundation 等]
    B -->|自定义库| D{使用场景}
    
    D -->|主 App 内部| E[允许<br/>Embed & Sign]
    D -->|App Extension| F[允许<br/>共享 Framework]
    D -->|跨 App 共享| G[ 不允许<br/>沙盒限制]
    
    style C fill:#98FB98
    style E fill:#98FB98
    style F fill:#98FB98
    style G fill:#FF6B6B

App Extension 共享 Framework

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

配置方法:

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

十、常见问题

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

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

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

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

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

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

# 查看动态库依赖
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 -->|基础工具库<br/>如 JSON 解析| C[静态库<br/>减少启动时间]
    B -->|UI 组件库<br/>频繁更新| D[动态库<br/>便于维护]
    B -->|大型 SDK<br/>如地图| E[动态库<br/>减少体积]
    B -->|核心业务库<br/>稳定| F[静态库<br/>提高性能]
    
    style C fill:#FFD93D
    style D fill:#FFA07A
    style E fill:#FFA07A
    style F fill:#FFD93D

性能优化建议

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

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

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

总结

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

一般建议:

  • 🎯 小型 App: 优先使用静态库
  • 🎯 大型 App: 混合使用,核心用静态,功能用动态
  • 🎯 SDK 开发: 提供两种版本供开发者选择