在 Xcode,我们什么时候应该使用“嵌入式二进制文件”而不是“链接框架”?

关于 用库链接二进制 VS 嵌入式框架中描述的这两个选项之间的区别,有一个很好的问题。

似乎我们可以选择同时使用它们,只是不知道在哪种情况下我们应该更好地使用嵌入式二进制文件,还是更好地使用链接框架?

有什么实例可以更清楚地说明这一点吗? 谢谢

38171 次浏览

您链接的问题引用了“ Link Binary With Library”功能,这与嵌入式二进制文件有些不同。

“使用库链接二进制文件”意味着你所期望的链接: 不管二进制文件是静态库、动态库还是框架,它都会在编译后的链接时间链接到你的目标代码。

当您考虑与静态库进行链接时,会发生什么是非常清楚的: 链接器 副本将库中的代码(例如 libFoo.a)链接到输出二进制文件中。输出文件的大小不断增长,但不需要在运行时解析任何外部依赖项。程序需要运行的所有东西(关于静态库)在构建之后都会出现。

使用动态库(。Dylib,或者系统提供的框架) ,期望在您运行程序时,您链接到的库将出现在系统的动态库加载程序路径中的某个位置。这样你就不用把所有的第三方外部库拷贝到你的二进制文件中,而且计算机上所有不同的程序都可以找到它,这样可以节省最小的磁盘空间,但是也可能节省内存空间,这取决于系统缓存库的方式和位置。

框架非常类似于动态库,但是可以在其目录结构(图像、音频、其他框架等)中包含资源。在这种情况下,一个简单的静态库或。Dylib 文件不会删除它,因此您可能必须链接到一个框架,这样 就可以找到它需要正确运行的内容。

当您链接到第三方框架(比如您从 github 下载并自己构建的某个框架)时,它可能不会出现在您想要运行的系统上。在这种情况下,你不仅要链接到框架,还要使用“复制框架”阶段把它嵌入到你的捆绑中。当你的程序运行时,运行时链接器(也称为解析器)除了查看系统加载器路径之外,还会查看你的捆绑包,找到嵌入式框架,然后链接它,这样你的应用程序就有了运行所需的代码。

最后,真正的“嵌入式二进制文件”是一个可执行文件,你们通过拷贝文件阶段嵌入到你们的捆绑中,然后你们自己执行,也许通过调用 popen()或类似的方式。嵌入的二进制文件可以被你的程序调用,但是它不与之链接。它是一个完全外部实体(如 /bin目录中的程序)。

实际上,对于系统提供的库和框架,您将链接到它们,这就是您需要做的全部事情。

如果您需要链接您构建的不需要任何嵌入式资源(即不需要框架存在)的库,那么您可以直接链接到静态库。如果您发现程序中有多个模块希望使用相同的库代码,那么将其转换为一个框架或动态库并进行相应的链接可以节省空间并且可能很方便(特别是如果内存使用是一个问题)。

最后,框架不仅可以包含资源,还可以包含头文件和/或许可文件。使用框架来传递这些文件实际上是一种方便的发布机制,所以你可能经常需要合并一个框架,这样这些东西就可以和你的二进制文件一起标记(也就是说,许可证要求可能使这成为强制性的)。

- 编辑

亚当 · 约翰斯发表了以下问题作为评论:

这是个好答案。不过有件事我还是有点困惑。自己执行二进制代码意味着什么?你的意思是简单地使用嵌入式框架的代码吗?我知道你提到了 popen () ,但你是说我的应用程序正在调用 popen () ?我不知道那是什么意思。

我是说 嵌入式二进制只是捆绑包中的另一个资源文件,就像音频文件或图像一样,尽管该文件是一个可执行命令行工具。popen()函数(从终端读取更多信息的 man popen)允许您从另一个正在运行的程序执行任意程序。system()函数是另一种方式。还有其他的例子,我将在这里给出一个历史例子,它可以让我们更清楚地理解嵌入式二进制文件的使用:

您可能已经知道,当您在 Mac OS X 上启动一个应用程序时,它是以当前用户的用户 ID 启动的。在大多数常见的安装下,默认用户是桌面上的 admin用户,他被赋予用户 id 501

在基于 Unix 的操作系统上,只有 root用户(用户 id 0)可以完全访问整个文件系统。有时,桌面用户启动的安装程序需要将文件安装在特权目录中(例如驱动程序)。在这种情况下,应用程序需要将其特权升级到 root用户,以便能够在这些受限制的目录中进行写操作。

为了通过 OS X 10.7在操作系统中实现这一点,苹果在其 授权服务 API中提供了函数 用特权授权执行()(这个函数现在已经被废弃了,但仍然是一个有用的例子)。

AuthorizationExecuteWithPrivileges()将命令行工具的路径作为参数执行为 root。命令行工具是一个可执行的 shell 脚本或编译的二进制文件,您编写这些脚本来运行安装逻辑。这个工具就像其他资源文件一样安装在你的捆绑里。

当调用时,操作系统会显示一个授权对话框,询问用户的密码(您以前见过这个!)当输入将执行程序作为 root代表您的应用程序。这个过程类似于自己用 popen()执行一个程序,尽管单独使用 popen()并不能给你带来权限提升的好处。

简而言之,

  • 系统库,链接它们;
  • 第三方图书馆,嵌入它们。

为什么?

  • 如果您尝试嵌入系统库,您不会在弹出列表中找到它们;
  • 如果你链接第三方库,你可能会得到一个崩溃。

Xcode pre-v11. 嵌入式二进制文件与链接框架和库

历史

Embedded Binaries vs Linked Frameworks and Libraries -> Frameworks, Libraries, and Embedded Content

[ Xcode v11. frances,library,and insert Content ] 在 abc0选项卡的 Xcode v11部分中替换了它

embedded binariesLinked FrameworksDependency管理 < sup > [ About ] 的一部分

[ Xcode v11]

链接二进制

General -> Linked Frameworks and LibrariesBuild Phases -> Link Binary With Libraries的镜像。

静态库和框架

如果您添加一个 Static Library or Static Framework到这个部分,它将出现在 Frameworks 小组< sup > [ About ] (Project Navigator -> <workspace/project> -> Frameworks) ,并将有一个参考添加到您的项目为它。然后它将被 Static Linker使用。Static Linker在编译时将 包括/复制的所有代码从库中输入到可执行的对象文件中。Static linkerBuild Settings -> <Library/Framework> Search Paths配对工作

Static Library

Static Framework

  • 如果你不添加一个 static framework到这个部分,你会得到一个编译错误 [没有这样的模块]

嵌入二进制

静态库与静态框架

嵌入对于 Static LibraryStatic Framework没有任何意义,因为它们的符号被编译成可执行的二进制文件。Xcode 不允许在 Embed 部分下面删除 static library

动态框架

General -> Embedded BinariesBuild Phases -> Embed Frameworks的镜像。

实际上是将框架的 增加一份副本嵌入到你的捆绑中(而不是将框架和应用程序的代码合并成单个可执行的二进制文件)

默认情况下,bundle 的文件夹是 Frameworks,但是您可以使用 Destination字段更改它。此外,还可以指定 Subpath

Dynamic linker :dyld加载或运行时间将尝试找到的 嵌入式框架使用 @rpath< sup > [ About ] 如果没有找到的错误将发生 < sup > [ dyld: 库未加载]

结果:

  • Static Library-Link
  • Static Framework-Link
  • Dynamic Framework-Embed

[静态与动态链接器]
[使用链接和嵌入时]
[词汇]