在Xcode中,何时应该使用“嵌入式二进制文件”而不是“链接的框架”?

151

链接二进制文件和库与嵌入框架中,有一个很好的问题,问这两个选项之间的区别。

看起来我们可以两种都使用,只是想知道什么情况下我们应该更好地使用嵌入式二进制文件,或者是链接的框架?

有没有明确说明这一点的具体例子?谢谢。


可能是 Link Binary with libraries VS Embed Frameworks 的重复问题。 - Suhaib
3个回答

262
你提供的问题涉及到“链接二进制文件和库”的功能,这与嵌入式二进制文件有所不同。
“链接二进制文件和库”在链接方面意味着:无论二进制文件是静态库、动态库还是框架,在编译后的链接时都会将其链接到您的目标代码中。
当您考虑使用静态库进行链接时,发生的情况非常清楚:链接器将代码从库(例如libFoo.a)复制到输出二进制文件中。您的输出文件大小增加但不需要在运行时解决任何外部依赖关系。构建完成后,程序运行所需的所有内容(与静态库相关的内容)都已存在。
对于动态库(.dylib或系统提供的框架),期望的是您链接的库在运行程序时会出现在系统的动态库加载器路径的某个位置。这样,您就不必将所有第三方外部库复制到您的二进制文件中,并且计算机上所有连接到该库的不同程序都能够找到它,这最多可以节省磁盘空间,但也可能节省内存空间,具体取决于系统如何以及在何处缓存库。
一个框架很像一个动态库,但可以在其目录结构中包含资源(图像、音频、其他框架等)。在这种情况下,简单的静态库或dylib文件不足以满足需求,因此您可能需要链接到一个框架,以便它能够找到运行所需的内容。
当您链接到第三方框架(比如从GitHub下载并自己构建的框架)时,它可能不存在于您打算运行的系统上。在这种情况下,您不仅需要链接到框架,还要使用“复制框架”阶段将其嵌入应用程序包中。当您的程序运行时,运行时链接器(也称为解析器)将在您的包内查找嵌入式框架,并将其链接,以便您的应用程序具有运行所需的代码。
最后,正确的“嵌入式二进制文件”是一个您通过“复制文件”阶段嵌入到应用程序包中的可执行文件,并且您自己执行它,例如使用popen()或类似的调用。嵌入式二进制文件可能会被您的程序调用,但它与之无关联。它是一个完全外部的实体(就像/bin目录中的程序)。
实际上,对于系统提供的库和框架,您将链接到它们,这就是您需要做的全部。
如果您需要链接一个不需要任何嵌入式资源(即不需要存在框架)的构建库,则可以直接链接到静态库。如果您发现程序中有多个模块想要使用相同的库代码,则将其转换为框架或动态库,并链接到该库可以节省空间并且可能很方便(特别是如果内存使用是一个问题)。
最后,框架不仅可以包含资源,还可以包括头文件和/或许可证文件。使用框架传达这些文件实际上是一种方便的分发机制,因此通常您可能希望将框架合并,以便这些文件可以随着您的二进制文件一起传递(即许可证要求可能会强制执行此操作)。
---编辑---
Adam Johns在评论中发布了以下问题:
这是一个很好的答案。但我还是有点困惑。执行自己的二进制文件是什么意思?您是否意味着只是使用嵌入式框架的代码?我知道您提到了popen(),但您是说我的应用程序正在调用popen()吗?我不太明白这是什么意思。
我在说的是嵌入式二进制文件只是你包中的另一个资源文件,就像音频文件或图像一样,尽管该文件实际上是可执行的命令行工具。 popen() 函数(从您的终端键入 man popen 以了解更多信息)允许您从另一个正在运行的程序中执行任意程序。 system() 函数是另一种方式。还有其他方法,在这里我将给出一个历史性的例子,可能会使使用嵌入式二进制文件的理解更加清晰:
正如您可能已经意识到的那样,当您在 Mac OS X 上启动应用程序时,它会以当前用户的用户 ID 启动。在大多数常见的安装中,这是默认的桌面管理员用户,其用户 ID 为 501
在基于 Unix 的操作系统上,只有 root 用户(用户 ID 为 0)可以完全访问整个文件系统。有时发生这样的情况,由桌面用户启动的安装程序需要在受限目录中安装文件(例如驱动程序)。在这种情况下,应用程序需要提升其权限以成为 root 用户,以便可以写入这些受限制的目录。
为了在操作系统中实现这一点,从OS X 10.7开始,苹果提供了Authorization Services API函数AuthorizationExecuteWithPrivileges()(虽然现在已被弃用,但仍是一个有用的示例)。 AuthorizationExecuteWithPrivileges()接受一个路径作为参数,该路径指向要以root身份执行的命令行工具。命令行工具可以是你编写的可执行shell脚本或编译后的二进制文件,用于运行安装逻辑。这个工具被安装在应用程序包内,就像任何其他资源文件一样。
调用时,操作系统会弹出一个授权对话框,要求用户输入密码(您之前见过这个!),并在输入后代表您的应用程序以root身份执行程序。这个过程类似于自己使用popen()执行程序,尽管popen()本身并不能让您获得特权升级的好处。

78
你是怎么知道这些事情的? - Ian Warburton
68
我已经在苹果操作系统上编程超过20年,学到了一些零碎的知识。 :) - par
1
@JustAMartin 我指的是 link,但你说得对,你还必须通过复制文件阶段嵌入它(否则你怎么使用它呢?)。使用第三方框架或嵌入式二进制文件的目标是执行实体提供的代码。使用嵌入式二进制文件时不涉及链接。在运行时,您构造一个到二进制文件的路径,然后手动执行它。使用框架时,编译时链接器将在构建应用程序时将其链接,然后(如果它是第三方框架)通过复制文件阶段嵌入它,最后,在运行应用程序时运行时链接器再次链接它。 - par
1
事情有点不清楚,关于你对@JustAMartin的回答。使用第三方框架或嵌入式二进制文件的目的是执行该实体提供的代码。现在,嵌入式二进制文件也可以是第三方框架。我正在尝试理解你在这里的意思...据我所知,嵌入式二进制文件意味着将嵌入的框架的单独二进制文件引入到应用程序包中,如果您只链接相同的框架,则会将其放入与应用程序相同的二进制文件中。如果我错了,请纠正我... - hariszaman
1
也许有新的Xcode魔法可以加载嵌入式框架。我已经有一段时间没有需要那个功能了。如果你想更深入地探索这里发生了什么,请在SO上发布一个新问题。 - par
显示剩余10条评论

38

简而言之,

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

为什么?

  • 如果您尝试嵌入系统库,您将无法在弹出列表中找到它们;
  • 如果您链接第三方库,可能会导致崩溃。

12

Xcode v11之前版本中,嵌入式二进制文件与链接的框架和库

历史

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

[Xcode v11. 框架、库和嵌入式内容]已替换为General选项卡中的Xcode v11部分

嵌入式二进制文件(embedded binaries)链接框架(Linked Frameworks)依赖管理(Dependency)的一部分[关于]

[Xcode v11]

链接二进制文件(Link Binary)

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

静态库和框架

如果您将静态库或静态框架添加到此部分,则它将显示在Frameworks[关于](项目导航器(Project Navigator) -> <工作区/项目> -> Frameworks)中,并将为其添加一个引用。然后它将被静态链接器(Static Linker)使用。编译时,静态链接器会将库中的所有代码包含/复制到可执行对象文件中。静态链接器构建设置(Build Settings) -> <Library/Framework> Search Paths配对工作。

静态库

静态框架

  • 构建设置 -> 框架搜索路径。如果您不将静态框架添加到此部分,您将收到编译错误[No such module]

嵌入式二进制文件

静态库和静态框架

对于静态库静态框架而言,嵌入并没有任何意义,因为它们的符号被编译到可执行二进制文件中。Xcode不允许您在嵌入部分下放置静态库

动态框架

常规 -> 嵌入式二进制文件构建阶段 -> 嵌入框架的镜像。

嵌入实际上是向您的应用程序包中添加框架的副本(而不是将框架和应用程序的代码合并为单个可执行二进制文件)

默认情况下,该捆绑文件夹为Frameworks,但您可以使用Destination字段更改它。此外,您可以指定一个Subpath

动态链接器:dyld加载或运行时会尝试使用@rpath[关于]查找嵌入式框架。如果没有找到,将出现错误[dyld: Library not loaded]

结果:

  • 静态库 - 链接
  • 静态框架 - 链接
  • 动态框架 - 嵌入

[静态链接器 vs 动态链接器]
[何时使用链接和嵌入]
[术语表]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接