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 文件保存在其中?

4

1 回答 1

65

我认为您误解了错误消息。

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

dyld在运行时,您可能会从动态链接库加载器中收到该错误消息的原因有几个。第一个是您在构建过程中忘记将 .frameworks 复制到构建的应用程序包中。虽然它们可以复制到应用程序包内的任何位置,但传统的位置是在 AppName.app/Contents/Frameworks/ 中。如果您还没有这样做,请选择 Project > New Build Phase > New Copy Files Build Phase。将 Destination 弹出窗口更改为 Frameworks,如下图所示。

在此处输入图像描述

然后,您将框架的图标拖到文件夹中,以便在构建过程中复制它。

在此处输入图像描述

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

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

在此处输入图像描述

以下摘录解释了如何在运行时找到动态库,摘自手册页dyld

动态库加载

与许多其他操作系统不同,Darwin 不会通过叶子文件名来定位依赖的动态库。而是使用每个 dylib 的完整路径(例如 /usr/lib/libSystem.B.dylib)。但有时完整路径并不合适;例如,可能希望您的二进制文件可以安装在磁盘上的任何位置。为了支持这一点,可以使用三个 @xxx/变量作为路径前缀。在运行时dyld 用动态生成的路径替换@xxx/前缀。

@executable_path/

此变量被替换为包含进程的主要可执行文件的目录的路径。这对于加载 .app 目录中嵌入的 dylibs/frameworks 很有用。如果主可执行文件是 at/some/path/My.app/Contents/MacOS/My 并且框架 dylib 文件是 at
/some/path/My.app/Contents/Frameworks/Foo.framework/Versions/A/Foo,则框架加载路径可以编码为 @executable_path/../Frameworks/Foo.framework/Versions/A/Foo .app 目录可以在文件系统中移动,并且dyld仍然能够加载嵌入式框架。

@loader_path/

此变量被替换为包含 mach-o 二进制文件的目录的路径,该文件包含使用 @loader_path. 因此,在每个二进制文件中,@loader_path解析到不同的路径,而@executable_path总是解析到相同的路径。@loader_path如果插件的最终文件系统位置未知(因此不能使用绝对路径)或插件被多个应用程序使用(所以 @executable_path 不能使用)。如果插件 mach-o 文件是 at /some/path/Myfilter.plugin/Contents/MacOS/Myfilter并且框架 dylib 文件是 at /some/path/Myfilter.plugin/Contents/Frameworks/Foo.framework/Versions/A/Foo,那么框架加载路径可以编码 @loader_path/../Frameworks/Foo.framework/Versions/A/FooMyfilter.plugin目录可以在文件系统中移动,并且dyld仍然能够加载嵌入式框架。

@rpath/

Dyld 维护一个称为运行路径列表的当前路径堆栈。当 @rpath 遇到时,它会被运行路径列表中的每个路径替换,直到找到可加载的 dylib。运行路径堆栈是从LC_RPATH导致当前 dylib 加载的依赖链中的加载命令构建的。您可以使用(1)的选项向LC_RPATH图像添加加载命令。你甚至可以添加一个以 . 开头的加载命令路径, 它会在运行路径堆栈上推送一个相对于包含 . 指某东西的用途-rpathldLC_RPATH@loader_path/LC_RPATH@rpath当您具有可以安装在任何地方但保持其相对位置的程序和 dylib 的复杂目录结构时,这是最有用的。这个场景可以使用 实现@loader_path,但是 dylib 的每个客户端可能需要不同的加载路径,因为它在文件系统中的相对位置不同。的使用 @rpath 引入了简化事情的间接级别。您在目录结构中选择一个位置作为锚点。然后,每个 dylib 都会获得一个安装路径,@rpath该路径以 dylib 相对于锚点的路径开头。每个主要可执行文件都与 链接-rpath @loader_path/zzz,其中zzz是从可执行文件到锚点的路径。在运行时 dyld将其运行路径设置为锚点,然后相对于锚点找到每个 dylib。

于 2011-09-27T00:53:40.067 回答