Ich habe es mit einem seltsamen Verhalten von XCode zu tun:
%Vor% Grundsätzlich ignoriert es meine Runpath Search Path
( LD_RUNPATH_SEARCH_PATHS
) Konfiguration, die tatsächlich @loader_path/../Frameworks
ist.
Ich kann in diesem Moment kein eingebettetes Framework laden: /
otool
sagt
P.S. Wenn Sie sich fragen, ob ich die Kopie zur Framework-Build-Phase hinzugefügt habe, lautet die Antwort ja.
Das Problem besteht darin, dass beim Erstellen von SBJSON.framework
der Installationsname nicht für die Verwendung von @rpath
konfiguriert wurde. Wenn Ihre App die dynamische Bibliothek lädt, wird sie daher in /Library/Frameworks
angezeigt und nicht dort, wo der RUN-Pfad Ihrer App angezeigt wird.
Um es zu beheben, müssen Sie eine Build-Einstellung für das SBJSON.framework
-Ziel ändern. Ändern Sie die Einstellung Dynamic Library Install Name in @rpath/${EXECUTABLE_PATH}
. Dann baue es einfach neu, verlinke es mit dem neu erstellten Framework und du kannst loslegen.
Frameworks sind im Kern nur dynamische Bibliotheken. Dies bedeutet, dass der in der Bibliothek enthaltene Code nicht in Ihre App eingebettet ist, sondern aus dem Paket SBJSON.framework
zur Laufzeit abgerufen wurde. Um dies zu tun, muss Ihre App wissen, wo Sie nach der dynamischen Bibliothek suchen müssen.
Das funktioniert so, wenn der Linker beim Verknüpfen mit einem Framework nicht die gesamte Bibliothek in Ihre App einbindet. Stattdessen fügt es nur einen kleinen Abschnitt hinzu, der der App mitteilt, wo sie die Bibliothek finden soll, wenn sie ausgeführt wird. Das bedeutet natürlich, dass der Compiler wissen muss, wo sich die Bibliothek zur Laufzeit befindet. Es findet diese Informationen, indem es auf den "Install Name" der dynamischen Bibliothek schaut.
Der Installationsname einer dynamischen Bibliothek ist im Wesentlichen nur der Pfad, zu dem die Bibliothek erwartet wird. In der Vergangenheit wurden die meisten dynamischen Bibliotheken und Frameworks systemweit geteilt. Dinge wie Foundation.framework
und CoreData.framework
zum Beispiel leben in /System/Library/Frameworks
, und alle Apps können vernünftigerweise erwarten, sie dort zu finden. Wenn also CoreData.framework
erstellt wird, wird der Installationsname auf /System/Library/Frameworks/...
gesetzt. Wenn Xcode Ihre App mit Core Data verknüpft, wird der Installationsname des Frameworks angezeigt und es wird beim Start der App im Framework an diesen Pfad geladen.
Das ist alles gut und gut, aber es hilft Ihnen nicht, wenn Sie ein Framework in Ihre App einbetten müssen. Sie wissen nicht, wo sich die App zur Laufzeit auf dem System befinden wird. Der Benutzer kann es von /Applications
ausführen, aber es kann auch von ~/Downloads
ausgeführt werden. Es gibt keinen einzigen Pfad, den Sie als Installationsname angeben könnten, der zur Laufzeit immer korrekt auf das Framework zeigen würde.
Um damit umzugehen, können Sie den Installationsnamen des Frameworks auf @loader_path/../Frameworks
setzen. Wenn der dynamische Loader @loader_path
anzeigt, wird er durch den Pfad der aktuell geladenen App ersetzt. Wenn dieser Installationsname verwendet wird, kann das Framework im Ordner Frameworks
einer beliebigen App installiert werden.
Aber die Dinge sind immer noch nicht perfekt. Das Framework diktiert immer noch, wo es platziert werden soll. Wenn eine andere App beispielsweise das Framework stattdessen in einen Ordner " Libraries
" stellen möchte, ist es kein Glück. Das Framework ist verantwortlich dafür, wo es statt der App platziert werden kann. Dies ist eine Umkehrung des Abhängigkeitsbaums und ist nicht ideal. Die App sollte in der Lage sein, das Framework von überall dort zu laden, wo es es speichern will, egal was andere Frameworks machen.
So wurde in OS X 10.5 @rpath
eingeführt. Wenn der Installationsname einer Dylib oder eines Frameworks mit @rpath
beginnt, wendet sich der Loader und fragt die App, was "Runpath-Suchpfade" sind und ersetzt diese. Dadurch kann die App angeben, wo ihre Frameworks leben werden . Mit @rpath
delegiert das Framework die Entscheidung zurück an die App. Die App kann @loader_path
verwenden, wenn sie möchte, oder sie kann einen absoluten Pfad angeben, wenn sie dies möchte. Es könnte sogar einen freigegebenen Ordner angeben, der von einer ganzen Reihe von Apps verwendet wird.
Also, zu deinem Problem. Sie haben den Pfad für den Suchpfad der App korrekt auf @loader_path/../Frameworks
festgelegt. Der Installationsname des Frameworks verwendet jedoch nicht @rpath
; tatsächlich verwendet es immer noch einen fest codierten Pfad zu /Library/Frameworks/...
. Da der Installationsname des Frameworks nicht @rpath
verwendet, werden die RUN-Pfade der App nicht einmal konsultiert. Es versucht einfach, SBJSON aus dem Ordner /Library
zu verknüpfen. Da es nicht vorhanden ist, stürzt Ihre App ab, bevor sie gestartet wird.
Sie müssen den Installationsnamen des SBJSON-Frameworks ändern, um @rpath
zu verwenden. Setzen Sie die Einstellung Dynamic Library Install Name auf @rpath/${EXECUTABLE_PATH}
. ( ${EXECUTABLE_PATH}
ist der relative Pfad zur internen dynamischen Bibliothek aus dem Ordner, der das Framework enthält.)
Sobald Sie das Framework mit dem neuen Installationsnamen erstellt haben, sollten Sie in der Lage sein, eine Verknüpfung mit dem neuen Framework herzustellen, sicherzustellen, dass es in den Frameworks/
-Ordner des App-Bundles kopiert wird, und Sie können loslegen!
P.S. Wenn das nicht alles klar ist, hat Mike Ash eine ziemlich gute Rezension von @rpath
und Freunden gemacht. Sie können hier finden.
Tags und Links objective-c xcode cocoa sbjson