如何在Xcode 4中使用dylib文件创建一个可工作的框架

21

我在Xcode中创建了一个新的Cocoa框架,在开始时删除了所有库和文件,只留下支持文件。

我有2个文件:

add.h

#ifndef add_add_h
#define add_add_h

void add(void);

#endif
并且。
add.c
#include <stdio.h>
#include "add.h"

void add(void)
{
    printf("adfding");

}

在构建阶段,我将add.c添加到编译源文件中,并将add.h添加到公共头文件中。项目可以无问题地构建,但在框架中没有dylib文件,当我将框架拖放到另一个项目时,它会显示找不到dylib文件。

dyld: Library not loaded: @rpath/add.framework/Versions/A/add 
  Referenced from: /Users/vjoukov/Desktop/Projects/test/build/Debug/test.app/Contents/MacOS/test
  Reason: image not found

我该如何创建一个简单的框架并将dylib文件保留在其中?

1个回答

65

我认为你误解了错误信息。

.framework作为一个动态库工作,但在.framework文件夹内不会有任何具有实际.dylib文件扩展名的Mach-O可加载对象文件。

您可能会从运行时的dyld动态链接库加载器收到该错误消息的几个原因。首先,您可能忘记在构建过程中将.Frameworks复制到已构建应用程序包中。虽然它们可以被复制到应用程序包中的任何位置,但传统位置是在AppName.app/Contents/Frameworks/中。如果尚未这样做,请选择Project > New Build Phase > New Copy Files Build Phase。像下图中那样更改目标弹出菜单为Frameworks。

enter image description here

然后,您需要将Framework的图标拖到文件夹中,以便在构建过程中进行复制。

enter image description here

第二种更有可能导致框架无法在运行时找到的原因是您尚未为主可执行文件指定任何运行路径搜索路径(这是必需的,因为如您从错误消息中看到的,您的框架使用了更新的@rpath/样式安装名称(@rpath/add.framework/Versions/A/add),而不是旧的@executable_path/@loader_path/样式)。

只要您将自定义框架复制到上述位置,就可以添加@loader_path/../Frameworks的运行路径搜索路径条目,如下图所示:

enter image description here

以下节选解释了动态库在运行时如何被找到,来自于dyld的man页面:

动态库加载

与许多其他操作系统不同,Darwin不会定位通过其叶子文件名来链接依赖动态库。相反,每个dylib的完整路径都被使用(例如/usr/lib/libSystem.B.dylib)。但是有时候完整路径并不合适;例如,您希望在磁盘上的任何位置安装您的二进制文件。为了支持这一点,有三个可以用作路径前缀的@xxx/变量。运行时,dyld会用一个动态生成的路径替换@xxx/前缀。 @executable_path/ 此变量将被替换为包含该进程主可执行文件的目录的路径。这对于加载嵌入在.app目录中的dylibs/frameworks很有用。如果主可执行文件位于/some/path/My.app/Contents/MacOS/My,而框架dylib文件位于/some/path/My.app/Contents/Frameworks/Foo.framework/Versions/A/Foo,则框架加载路径可以编码为@executable_path/../Frameworks/Foo.framework/Versions/A/Foo,.app目录可以在文件系统中移动,并且dyld仍然能够加载嵌入的框架。 @loader_path/ 此变量将被替换为包含使用@loader_path的加载命令的mach-o二进制文件的目录的路径。因此,在每个二进制文件中,@loader_path解析为不同的路径,而@executable_path总是解析为相同的路径。@loader_path对于嵌入插件中的框架/dylib的加载路径很有用,如果最终文件系统中插件的位置可能会改变。插件位置未知(因此不能使用绝对路径),或者如果插件被多个应用程序使用(因此无法使用@executable_path)。 如果插件 mach-o 文件位于/some/path/Myfilter.plugin/Contents/MacOS/Myfilter,并且框架 dylib 文件位于/some/path/Myfilter.plugin/Contents/Frameworks/Foo.framework/Versions/A/Foo,则框架加载路径可以编码为@loader_path/../Frameworks/Foo.framework/Versions/A/Foo,并且Myfilter.plugin目录可以在文件系统中移动,dyld仍然能够加载嵌入的框架。

当遇到@rpath时,Dyld会维护一个当前路径堆栈,称为运行路径列表。 它将@rpath替换为每个运行路径列表中的路径,直到找到可载入的dylib。 运行路径堆栈是从导致当前dylib加载的依赖关系链中的LC_RPATH加载命令构建的。 您可以使用-rpath选项向图像添加LC_RPATH加载命令。 您甚至可以添加以@loader_path/开头的LC_RPATH加载命令路径,并且它将推送相对于包含LC_RPATH的图像的路径到运行路径堆栈中。 当您具有程序和dylib的复杂目录结构,可以将它们安装在任何位置,但保持它们的相对位置时,使用@rpath最有用。可以使用@loader_path来实现,但是每个动态库的客户端需要不同的加载路径,因为它们在文件系统中的相对位置不同。使用@rpath引入了一层简化事物的间接性。您将一个位置作为锚点,在您的目录结构中选择一个位置作为锚点。然后,每个dylib都会得到一个安装路径,该安装路径以@rpath开头,是相对于锚点的dylib路径。每个主可执行文件都链接了-rpath @loader_path/zzz,其中zzz是从可执行文件到锚点的路径。运行时dyld设置其运行路径为锚点,然后每个dylib相对于锚点被找到。

5
哇!这真是一个非常完整的答案,它还解决了我遇到的问题!谢谢!! - El Developer
非常全面和完整的答案... 做得好! - Kaelin Colclasure
尽管这个答案非常完整 - 它主要涵盖了可执行文件(App)与其框架(或其他dylib)的关系。我需要从几个.dylibs构建一个单独的框架(应该嵌入在该框架中),但我的可执行文件在嵌入式dylibs中的所有符号上都出现了链接错误。我正在尝试理解如何从几个dylibs组成框架 - 但似乎没有人写过任何关于此的东西。 - Motti Shneor
@NSGod,如果你遇到这种情况,你会怎么做:http://stackoverflow.com/questions/40318465/setting-the-search-path-for-plug-in-dylib?谢谢。 - Royi

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