我们啥时候应该在 Xcode 中使用“嵌入式二进制文件”而不是“链接框架”?
Posted
技术标签:
【中文标题】我们啥时候应该在 Xcode 中使用“嵌入式二进制文件”而不是“链接框架”?【英文标题】:When should we use "embedded binaries" rather than "Linked Frameworks" in Xcode?我们什么时候应该在 Xcode 中使用“嵌入式二进制文件”而不是“链接框架”? 【发布时间】:2015-12-16 23:24:55 【问题描述】:关于Link Binary with libraries VS Embed Frameworks 中描述的这两个选项之间的区别有一个很好的问题。
似乎我们可以选择同时使用它们,只是想知道哪种情况下我们应该更好地使用嵌入式二进制文件,还是使用链接框架?
有什么可靠的例子可以更清楚地解决这个问题吗?谢谢
【问题讨论】:
Link Binary with libraries VS Embed Frameworks的可能重复 【参考方案1】:您链接的问题引用了“Link Binary With Libraries”功能,这与嵌入式二进制文件有些不同。
“Link Binary With Libraries”表示您对链接的期望:无论二进制文件是静态库、动态库还是框架,它都会在编译后的链接时链接到您的目标代码.
当您想到与静态库的链接时,会发生什么很清楚:链接器将库中的代码(例如 libFoo.a
)复制到您的输出二进制文件中。您的输出文件会变大,但不需要在运行时解析任何外部依赖项。您的程序需要运行的所有内容(相对于静态库)在构建后都已存在。
对于动态库(.dylib 或系统提供的框架),预期当您运行程序时,您要链接的库将出现在系统的动态库加载器路径中的某个位置。这样您就无需将所有第三方外部库复制到您的二进制文件中,并且计算机上所有链接到该库的不同程序都可以找到它,从而节省了最少的磁盘空间,而且潜在的内存空间,具体取决于系统缓存库的方式和位置。
框架很像动态库,但可以在其目录结构中包含资源(图像、音频、其他框架等)。在这种情况下,一个简单的静态库或 .dylib 文件不会删除它,因此您可能必须链接到一个框架,以便 它 可以找到它正常运行所需的内容。
当您链接到第三方框架(比如您从 github 下载并自己构建的框架)时,它可能不会出现在您打算运行的系统上。在这种情况下,您不仅要链接到框架,还要使用“复制框架”阶段将其嵌入到您的应用程序包中。当您的程序运行时,运行时链接器(也称为解析器)将在您的包内查找系统加载程序路径,找到嵌入式框架,并将其链接,以便您的应用程序将拥有运行所需的代码。
最后,正确的“嵌入式二进制文件”是一个可执行文件,您可以通过 Copy-Files Phase 将它们嵌入到您的应用程序包中,并且您自己执行,可能需要调用popen()
或类似的方法。嵌入的二进制文件可能会被您的程序调用,但它没有与之链接。它是一个完全外部的实体(如/bin
目录中的程序)。
实际上,对于系统提供的库和框架,您将链接到它们,这就是您需要做的所有事情。
如果您需要链接您构建的不需要任何嵌入式资源的库(即不需要存在框架),那么您可以链接到静态库。如果您发现您的程序中有多个模块想要使用相同的库代码,那么将其转换为框架或动态库并与之链接可以节省空间并且可能很方便(特别是如果内存使用是一个问题)。
最后,框架不仅可以包含资源,还可以包含头文件和/或许可文件。使用框架来传递这些文件实际上是一种方便的分发机制,因此您可能经常想要合并一个框架,以便这些东西可以与您的二进制文件一起标记(即许可证要求可能使这成为强制性的)。
--- 编辑---
Adam Johns 发表了以下问题作为评论:
这是一个很好的答案。然而,有些事情我仍然有点困惑。自己执行二进制文件是什么意思?你的意思是简单地使用嵌入式框架的代码吗?我知道你提到了 popen(),但你是说我的应用正在调用 popen()?我真的不知道那是什么意思。
我是说嵌入的二进制文件 只是你的包中的另一个资源文件,比如音频文件或图像,尽管该文件是一个可执行的命令行工具。 popen()
函数(man popen
从您的终端阅读更多信息)允许您从另一个正在运行的程序执行任意程序。 system()
函数是另一种方式。还有其他的,我将在这里给出一个历史示例,这可能会使嵌入式二进制文件的使用更加清晰:
您可能知道,当您在 Mac OS X 上启动应用程序时,它会使用当前用户的用户 ID 启动。在最常见的安装中,默认用户是桌面用户 admin
,用户 ID 为 501
。
在基于 Unix 的操作系统上,只有 root
用户(用户 id 0
)具有对整个文件系统的完全访问权限。有时,桌面用户启动的安装程序需要在特权目录(例如驱动程序)中安装文件。在这种情况下,应用程序需要将其权限提升到root
用户,以便它可以写入这些受限目录。
为了通过 OS X 10.7 在操作系统中实现这一点,Apple 在其 Authorization Services API 中提供了函数 AuthorizationExecuteWithPrivileges()(现在已弃用,但仍然是一个有用的示例)。
AuthorizationExecuteWithPrivileges()
将命令行工具的路径作为参数作为root
执行。命令行工具是您为运行安装逻辑而编写的可执行 shell 脚本或编译后的二进制文件。该工具安装在您的应用程序包中,就像任何其他资源文件一样。
当被调用时,操作系统会弹出一个授权对话框,询问用户的密码(你以前见过这个!),当输入时,它将代表你的应用程序以root
的身份执行程序。这个过程类似于只使用popen()
自己执行一个程序,尽管单独使用popen()
并不能为您带来特权提升的好处。
【讨论】:
你是怎么知道这些事情的? @IanWarburton 我已经为 Apple 操作系统编程 20 多年,并且在这里和那里收集了一些花絮。 :) @JustAMartin 我的意思是link
,但你是正确的,你还必须通过复制文件阶段嵌入它(否则你将如何使用它?)。使用第三方框架或嵌入式二进制文件的目的是执行实体提供的代码。使用嵌入式二进制不涉及链接。在运行时,您构建二进制文件的路径,然后手动执行它。使用框架,编译时链接器将在您构建应用程序时链接它,然后(如果它是第 3 方框架)您通过复制文件阶段嵌入它,最后运行时链接器在您运行应用程序时再次链接它.
您对@JustAMartin 的回答有些不清楚。使用第三方框架或嵌入式二进制文件的目的是执行实体提供的代码。如今,嵌入式二进制文件也可以是第三方框架。我试图理解你在这里的意思... AFA 我理解,嵌入式二进制文件的意思是,嵌入框架的单独二进制文件将被引入 App 包中,如果你只是链接相同的框架,它会将其放入相同的二进制文件中应用程序。如果我错了,请纠正我...
也许有新的 Xcode 魔法可以加载嵌入式框架。自从我需要那个功能以来已经有一段时间了。如果您想了解更多信息,请在此处发布一个新问题。【参考方案2】:
总之,
系统库,链接它们; 第三方库,嵌入它们。
为什么?
如果您尝试嵌入系统库,您将无法在弹出列表中找到它们; 如果您链接第 3 方库,您可能会遇到崩溃。
【讨论】:
【参考方案3】:Xcode v11 之前的版本。嵌入式二进制文件与链接框架和库
历史
Embedded Binaries vs Linked Frameworks and Libraries -> Frameworks, Libraries, and Embedded Content
[Xcode v11. Frameworks, Libraries, and Embedded Content] 将其从 General
选项卡中的 Xcode v11 部分替换
embedded binaries
和Linked Frameworks
是Dependency
管理[About] 的一部分
[Xcode v11]
链接二进制
General -> Linked Frameworks and Libraries
是Build Phases -> Link Binary With Libraries
的镜像。
静态库和框架
如果您将Static Library or Static Framework
添加到此部分,它将出现在Frameworks
组[About](Project Navigator -> <workspace/project> -> Frameworks
) 中,并且会为您的项目添加一个参考。然后它将被Static Linker
使用。 Static Linker
在编译时会将库中的所有代码包含/复制到可执行目标文件中。 Static linker
与 Build Settings -> <Library/Framework> Search Paths
配对使用
Static Library
Build Settings -> Library Search Paths
[library not found] 如果你不添加static library
到这个部分你会得到一个链接器错误[ld: symbol(s) not found]
Static Framework
Build Settings -> Framework Search Paths
。如果你没有在这个部分添加static framework
,你会得到一个编译错误[No such module]
嵌入二进制
静态库和静态框架
嵌入对于Static Library
和Static Framework
没有任何意义,因为它们中的符号被编译成可执行二进制文件。 Xcode 不会让您在嵌入部分下删除static library
。
动态框架
General -> Embedded Binaries
是Build Phases -> Embed Frameworks
的镜像。
嵌入实际上将框架的副本添加到您的应用程序包中(不将框架和应用程序的代码合并到单个可执行二进制文件中)
默认情况下,捆绑包的文件夹是Frameworks
,但您可以使用Destination
字段更改它。此外,您可以指定Subpath
。
Dynamic linker :dyld
在加载或运行时 将尝试使用@rpath
[About] 找到嵌入式框架发生[dyld: Library not loaded]
结果:
Static Library
- Link
Static Framework
- Link
Dynamic Framework
- Embed
[Static vs Dynamic linker][When use Link and Embed][Vocabulary]
【讨论】:
以上是关于我们啥时候应该在 Xcode 中使用“嵌入式二进制文件”而不是“链接框架”?的主要内容,如果未能解决你的问题,请参考以下文章
删除“DerivedData”后,Xcode 不会添加“嵌入式二进制”