XCode 4.4.1忽略LD_RUNPATH_SEARCH_PATHS

kil*_*anc 7 xcode cocoa objective-c sbjson

我正在处理XCode的奇怪行为:

dyld: Library not loaded: /Library/Frameworks/SBJson.framework/Versions/A/SBJson
Run Code Online (Sandbox Code Playgroud)

基本上它忽略了实际的my 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)
Run Code Online (Sandbox Code Playgroud)

PS如果您想知道我是否将副本添加到框架构建阶段,答案是肯定的.

BJ *_*mer 19

简短的版本

问题是,在构建时SBJSON.framework,安装名称未配置为使用@rpath.因此,当您的应用程序加载动态库时,它会查找/Library/Frameworks而不是应用程序的运行路径告诉它的位置.

要修复它,您需要更改SBJSON.framework目标上的构建设置.将动态库安装名称设置更改为@rpath/${EXECUTABLE_PATH}.然后再次构建,链接新建的框架,你很高兴.

长版

框架的核心是动态库.这意味着库中包含的代码不会嵌入到您的应用程序中,而是在运行时SBJSON.framework包中提取.为此,您的应用需要知道在哪里查找动态库.

这种方式的工作方式是,当您链接到框架时,链接器实际上并没有将整个库嵌入到您的应用程序中.相反,它只是添加了一个小部分,告诉应用程序在运行时在哪里找到库.当然,这意味着编译器必须知道库在运行时的位置.它通过查看动态库的"安装名称"来查找信息.

动态库的"安装名称"基本上只是库预期的路径.从历史上看,大多数动态库和框架都是在系统范围内共享的.比如像Foundation.frameworkCoreData.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}是包含框架的文件夹中内部动态库的相对路径.)

一旦你用新的安装名称构建了框架,你应该能够链接到新的框架,确保它被复制到app bundle的Frameworks/文件夹中,你很高兴!

PS如果不是很清楚的话,Mike Ash对@rpath朋友们做了很好的评论.你可以在这里找到它.