背景:

Unity 2019.3+ 引入了全新的 UnityFramework 架构:

旧架构 (Unity 2019.2 及更早)

└── Unity-iPhone.app
    └── Unity-iPhone (包含所有代码的可执行文件)

新架构 (Unity 2019.3+)

└── Unity-iPhone.app
    ├── Unity-iPhone (轻量级启动器)
    └── Frameworks/
        └── UnityFramework.framework (核心 Unity 代码存放处)

主要改进点

  1. Unity C# 代码 → 通过 IL2CPP 编译 → 最终生成 UnityFramework.framework
  2. Unity 原生插件 → 统一集成到 UnityFramework target
  3. Unity-iPhone 应用 → 仅作为轻量级启动外壳

iOS应用的限制

  • 应用沙盒限制:每个 iOS 应用都在自己的沙盒中运行,只能访问自己沙盒内的文件
  • 系统路径限制:不能像传统桌面系统那样将动态库安装在系统路径(如 /usr/lib)
  • 代码签名要求:所有可执行代码必须经过苹果的代码签名验证

OS应用沙盒机制要求,所有第三方动态库必须被嵌入到应用包内。如果只链接而未嵌入,应用在设备上启动时,系统在约定的位置找不到这个动态库,就会抛出@rpath相关的错误,也就是链接只在编译时有用,能够减少编译时间

为什么一定要添加到Unity-iPhone中

  • 应用包构建:unity-iPhone 是最终生成 .app 包的主 target
  • 资源包含:只有添加到 unity-iPhone 的资源才会被打包到最终应用中
  • 代码签名:所有嵌入的框架都需要与主应用一起签名

加载方式

unity-iPhone (主应用)
├── 应用启动代码
├── Info.plist
├── 资源文件
└── Frameworks/
    ├── UnityFramework.framework (动态库)
    └── YourCustomFramework.framework (你的动态库) ← 必须在这里

链接三方库和嵌入三方库的不同

  • 链接:在Build Phases的 Link Binary With Libraries阶段。这步是告诉编译器:“我这个程序需要这些库”。
  • 嵌入:在Build Phases的 Embed Frameworks阶段。这步是告诉打包系统:“请把这些动态库复制到最终的应用包(.app)里”。

Cocos 与 Unity的 target的不同

Cocos的单一Target结构:Cocos Creator打包后通常生成一个主Target(例如mygame-mobile)。当你使用use_frameworks!让CocoaPods以动态框架形式集成库时,这些框架会被自动添加到这个主Target中。CocoaPods的脚本通常会自动配置好编译时的链接和运行时的嵌入,所以即使有动态库,一般也能正常运行,不容易出现@rpath错误

动态库与静态库的区别

🔄 动态库与静态库的核心区别

代码加载机制差异

特性 静态库 动态库
内存布局 代码嵌入主可执行文件 独立内存段,支持共享
加载时机 应用启动时完整加载 支持按需延迟加载
代码共享 每个进程独享副本 多进程共享同一物理内存
热更新 不支持 理论可行(iOS受限)
耦合度 紧密耦合 松散耦合

内存占用对比

静态库多进程之间引用才会复制多份,单进程多target并不会多份

// 静态库场景:3进程共用静态库
进程A: [主程序 + 静态库副本A]
进程B: [主程序 + 静态库副本B] 
进程C: [主程序 + 静态库副本C]
总内存 = 3 × (主程序 + 静态库)

// 动态库场景:3进程共用动态库
进程A: [主程序] → 共享动态库
进程B: [主程序] → 共享动态库
进程C: [主程序] → 共享动态库
总内存 = 3 × 主程序 + 1 × 动态库

📱 iOS采用动态库的三大优势

系统框架高效共享,核心系统框架均为动态库:
UIKit.framework
Foundation.framework  
CoreGraphics.framework
静态库方案会导致:
  • 重复占用磁盘空间
  • 内存使用效率低下
  • 系统更新复杂化
  • 扩展架构支持
    • 主应用与扩展共享库资源
    • 避免多份静态库副本
    • 显著优化存储和内存
模块化开发支持
// 独立模块开发架构
AppModuleA.framework    // Team A
AppModuleB.framework    // Team B
AppModuleC.framework    // Team C

// 优势:
// - 团队独立开发测试
// - 二进制级别集成
// - 版本灵活管理

如何选择

  1. AppDelegate 相关功能 → 归入 Unity-iPhone 目标

  2. URL 与推送通知处理 → 归入 Unity-iPhone 目标

  3. 纯 UI 界面(非游戏相关) → 归入 Unity-iPhone 目标

  4. Unity C# 调用的 DllImport 方法 → 添加至所有目标(addToAllTargets)

  5. UnitySendMessage 相关调用 → 添加至所有目标(addToAllTargets)

  6. 不确定归属的功能 → 建议添加至所有目标(最稳妥方案)

podfile 中的链接标签

  1. 传统静态库(无特殊配置)

    • 打包方式:所有代码 → 生成 .a 文件 → 直接链接到主应用
    • 优点:⚡ 性能最优
    • 缺点:❌ 完全不支持 Swift(致命缺陷),
    • 稍微增加包体大小,不是缺陷
  2. 动态框架(use_frameworks!)

    • 打包方式: 生成 .framework 动态库 
    • 优点:✅ 完整支持 Swift
    • 缺点:🐌 启动时间增加 35-60%(尤其当加载 30+ 个框架时)
  3. 静态框架(use_frameworks! :linkage => :static)

    • 打包方式:.framework,强制使用静态库,内签到App内
    • 优点:✅ 支持 Swift  ⚡ 接近静态库的性能
  4. 动态框架(use_frameworks! :linkage => :dynamic)

  5. use_frameworks! 的生效范围

    • 三方库只能是源码的形式,有cocoapod进行编译
    • 对于podfile中包含了static_framework = true/false的三方库,不生效
    • 对于预编译好的静态库、或者动态库,不生效
    • 优先以三方库的设置为准

@rpath/报错

App加载Unity-iPhone时,在Framework中没找到对应的库。在

如果使用的是use_frameworks! :linkage => :static,说明该库尝试静态链接失败了,需要手动添加到Unity-iPhone这个target即可。

总结

1. 使用use_frameworks! :linkage => :static标签,获取更快地启动时间、支持swift

2. 如果使用EDM4U插件,dependency 里面使用addToAllTarget=true,防止动态库未静态链接成功,报@rpath错误

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐