我正在处理XCode的奇怪行为:
dyld: Library not loaded: /Library/Frameworks/SBJson.framework/Versions/A/SBJson
基本上它会忽略我的Runpath Search Path
(LD_RUNPATH_SEARCH_PATHS
)配置,实际上是@loader_path/../Frameworks
。
我现在无法加载任何嵌入式框架:/
otool
说
otool -L /Users/kilian/Library/Developer/Xcode/DerivedData/r-ghohkslxtxgpnuepmblogfjtuefx/Build/Products/Debug/r.app/Contents/MacOS/r
/Users/kilian/Library/Developer/Xcode/DerivedData/r-ghohkslxtxgpnuepmblogfjtuefx/Build/Products/Debug/r.app/Contents/MacOS/r:
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 19.0.0)
/Library/Frameworks/SBJson.framework/Versions/A/SBJson (compatibility version 1.0.0, current version 37.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 945.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1186.0.0)
P.S。如果您想知道我是否将副本添加到框架构建阶段,答案是肯定的。
答案 0 :(得分:19)
问题在于,当您构建SBJSON.framework
时,安装名称未配置为使用@rpath
。因此,当您的应用加载动态库时,它会查看/Library/Frameworks
而不是应用的运行路径告诉它的位置。
要解决此问题,您需要更改SBJSON.framework
目标上的构建设置。将动态库安装名称设置更改为@rpath/${EXECUTABLE_PATH}
。然后再次构建,链接新构建的框架,你很高兴。
框架的核心是动态库。这意味着库中包含的代码未嵌入到您的应用中,而是在运行时从SBJSON.framework
包中拉入。为此,您的应用需要知道在哪里寻找动态库。
这种方式的工作方式是,当您链接到框架时,链接器实际上并没有将整个库嵌入到您的应用程序中。相反,它只是添加了一个小部分,告诉应用程序在运行时在哪里找到库。当然,这意味着编译器必须知道库在运行时的位置。它通过查看动态库的“安装名称”来查找信息。
动态库的“安装名称”基本上只是库预期的路径。从历史上看,大多数动态库和框架都是在系统范围内共享的。例如,Foundation.framework
和CoreData.framework
等内容位于/System/Library/Frameworks
,所有应用都可以合理地期望在那里找到它们。因此,在构建CoreData.framework
时,其安装名称将设置为/System/Library/Frameworks/...
。当Xcode将您的应用与Core Data链接时,它会查看框架的安装名称,并告诉它在应用程序启动时加载到该路径的框架中。
这一切都很好,但是当你需要在你的应用程序中嵌入一个框架时,它并没有帮助你。您不知道应用程序在运行时将位于系统上的位置。用户可以从/Applications
运行它,但也可以从~/Downloads
运行它。您无法提供单个路径作为安装名称,它始终在运行时始终正确指向框架。
要处理此问题,您可以将框架的安装名称设置为@loader_path/../Frameworks
。当动态加载程序看到@loader_path
时,它会将其替换为当前加载的应用程序的路径。使用此安装名称将允许框架安装在任何应用程序的Frameworks
文件夹中。
然而,事情仍然不完美。该框架仍在决定应该放置的位置。例如,如果另一个应用程序想要将框架放在Libraries
文件夹中,那就不幸了。该框架负责放置它的位置,而不是应用程序。这是依赖树的反转,并不理想。应用程序应该能够从它想要隐藏它的地方加载框架,而不管其他框架是做什么的。
因此,在OS X 10.5中,引入了@rpath
。如果dylib或框架的安装名称以@rpath
开头,则加载器将转向并询问应用程序它的“Runpath搜索路径”是什么,并替换它们。这允许应用程序指定其框架的位置会活下去的通过使用@rpath
,框架将决策委托给应用程序。如果需要,应用可以使用@loader_path
,或者可以根据需要指定绝对路径。它甚至可以指定一整套应用程序将使用的共享文件夹。
所以,关于你的问题。您正确地将应用程序的运行路径搜索路径设置为@loader_path/../Frameworks
。但是,框架的安装名称未使用@rpath
;实际上,它仍在使用/Library/Frameworks/...
的硬编码路径。由于框架的安装名称不使用@rpath
,因此甚至不会查看应用程序的运行路径。它只是尝试从/Library
文件夹中链接SBJSON。由于它不存在,您的应用程序甚至会在启动之前崩溃。
您需要更改SBJSON框架的安装名称以使用@rpath
。将动态库安装名称设置为@rpath/${EXECUTABLE_PATH}
。 (${EXECUTABLE_PATH}
是内部动态库的相对路径,来自包含框架的文件夹。)
使用新安装名称构建框架后,您应该能够链接到新框架,确保将其复制到应用程序包的Frameworks/
文件夹中,您就可以开始了!
P.S。如果不是很清楚,Mike Ash对@rpath
和朋友们进行了很好的评论。你可以find it here。