Objective-C-Kategorien in der statischen Bibliothek

153

Können Sie mir zeigen, wie ich die statische Bibliothek richtig mit dem iPhone-Projekt verknüpfe? Ich verwende ein statisches Bibliotheksprojekt, das dem App-Projekt hinzugefügt wurde, als direkte Abhängigkeit (Ziel -> Allgemein -> Direkte Abhängigkeiten) und alles funktioniert in Ordnung, aber Kategorien. Eine in der statischen Bibliothek definierte Kategorie funktioniert in der App nicht.

Meine Frage ist also, wie man eine statische Bibliothek mit einigen Kategorien in ein anderes Projekt einfügt.

Und was ist im Allgemeinen die beste Vorgehensweise für App-Projektcode aus anderen Projekten?

Vladimir
quelle
1
Nun , ich habe einige Antworten gefunden und anscheinend wurde diese Frage hier bereits beantwortet (sorry, ich habe sie verpasst. stackoverflow.com/questions/932856/… )
Vladimir

Antworten:

227

Lösung: Ab Xcode 4.2 müssen Sie nur noch zu der Anwendung wechseln, die mit der Bibliothek verknüpft ist (nicht zur Bibliothek selbst), im Projektnavigator auf das Projekt klicken, auf das Ziel Ihrer App klicken, Einstellungen erstellen und dann nach "Andere" suchen Linker Flags ", klicken Sie auf die Schaltfläche + und fügen Sie '-ObjC' hinzu. '-all_load' und '-force_load' werden nicht mehr benötigt.

Details: Ich habe einige Antworten in verschiedenen Foren, Blogs und Apple-Dokumenten gefunden. Jetzt versuche ich, meine Suchanfragen und Experimente kurz zusammenzufassen.

Das Problem wurde verursacht durch (Zitat aus Apple Technische Fragen und Antworten QA1490 https://developer.apple.com/library/content/qa/qa1490/_index.html ):

Objective-C definiert nicht Linkersymbole für jede Funktion (oder Methode in Objective-C). Stattdessen werden Linkersymbole nur für jede Klasse generiert. Wenn Sie eine bereits vorhandene Klasse um Kategorien erweitern, kann der Linker den Objektcode der Kernklassenimplementierung und der Kategorieimplementierung nicht zuordnen. Dadurch wird verhindert, dass in der resultierenden Anwendung erstellte Objekte auf einen in der Kategorie definierten Selektor reagieren.

Und ihre Lösung:

Um dieses Problem zu beheben, sollte die statische Bibliothek die Option -ObjC an den Linker übergeben. Dieses Flag bewirkt, dass der Linker jede Objektdatei in die Bibliothek lädt, die eine Objective-C-Klasse oder -Kategorie definiert. Diese Option führt normalerweise zu einer größeren ausführbaren Datei (aufgrund des in die Anwendung geladenen zusätzlichen Objektcodes). Sie ermöglicht jedoch die erfolgreiche Erstellung effektiver statischer Objective-C-Bibliotheken, die Kategorien für vorhandene Klassen enthalten.

und es gibt auch Empfehlungen in den iPhone Development FAQ:

Wie verknüpfe ich alle Objective-C-Klassen in einer statischen Bibliothek? Setzen Sie die Build-Einstellung Other Linker Flags auf -ObjC.

und Flaggenbeschreibungen:

- all_load Lädt alle Mitglieder statischer Archivbibliotheken.

- ObjC Lädt alle Mitglieder statischer Archivbibliotheken, die eine Objective-C-Klasse oder -Kategorie implementieren.

- force_load (path_to_archive) Lädt alle Mitglieder der angegebenen statischen Archivbibliothek. Hinweis: -all_load erzwingt das Laden aller Mitglieder aller Archive. Mit dieser Option können Sie auf ein bestimmtes Archiv abzielen.

* Wir können force_load verwenden, um die Binärgröße der App zu reduzieren und Konflikte zu vermeiden, die all_load in einigen Fällen verursachen kann.

Ja, es funktioniert mit * .a-Dateien, die dem Projekt hinzugefügt wurden. Ich hatte jedoch Probleme mit dem lib-Projekt, das als direkte Abhängigkeit hinzugefügt wurde. Aber später stellte ich fest, dass es meine Schuld war - das direkte Abhängigkeitsprojekt wurde möglicherweise nicht richtig hinzugefügt. Wenn ich es entferne und mit Schritten wieder hinzufüge:

  1. Ziehen Sie die lib-Projektdatei per Drag & Drop in das App-Projekt (oder fügen Sie sie mit Projekt-> Zum Projekt hinzufügen… hinzu).
  2. Klicken Sie auf den Pfeil am lib-Projektsymbol - der Dateiname mylib.a wird angezeigt, ziehen Sie diese Datei mylib.a und legen Sie sie in der Gruppe Ziel -> Binär mit Bibliothek verknüpfen ab.
  3. Öffnen Sie die Zielinformationen auf der ersten Seite (Allgemein) und fügen Sie meine Bibliothek zur Abhängigkeitsliste hinzu

danach funktioniert alles OK. "-ObjC" -Flag war in meinem Fall genug.

Ich war auch an einer Idee aus dem Blog http://iphonedevelopmentexperiences.blogspot.com/2010/03/categories-in-static-library.html interessiert . Der Autor sagt, er kann die Kategorie aus lib verwenden, ohne das Flag -all_load oder -ObjC zu setzen. Er fügt der Kategorie h / m-Dateien nur leere Dummy-Klassenschnittstellen / Implementierungen hinzu, um den Linker zu zwingen, diese Datei zu verwenden. Und ja, dieser Trick macht den Job.

Der Autor sagte aber auch, er habe sogar kein Dummy-Objekt instanziiert. Mm ... Wie ich festgestellt habe, sollten wir explizit einen "echten" Code aus der Kategoriedatei aufrufen. Es sollte also zumindest die Klassenfunktion aufgerufen werden. Und wir brauchen sogar keine Dummy-Klasse. Einzelne c-Funktionen machen dasselbe.

Wenn wir also lib-Dateien schreiben als:

// mylib.h
void useMyLib();

@interface NSObject (Logger)
-(void)logSelf;
@end


// mylib.m
void useMyLib(){
    NSLog(@"do nothing, just for make mylib linked");
}


@implementation NSObject (Logger)
-(void)logSelf{
    NSLog(@"self is:%@", [self description]);
}
@end

und wenn wir useMyLib () aufrufen; Überall im App-Projekt und dann in jeder Klasse können wir die logSelf-Kategoriemethode verwenden.

[self logSelf];

Und noch mehr Blogs zum Thema:

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html

Vladimir
quelle
8
Der technische Hinweis von Apple wurde anscheinend dahingehend geändert, dass "Um dieses Problem zu beheben, muss die Zielverknüpfung mit der statischen Bibliothek die Option -ObjC an den Linker übergeben." Das ist das Gegenteil von dem, was oben zitiert wurde. Wir haben gerade bestätigt, dass Sie beim Verknüpfen der App und nicht der Bibliothek selbst angeben müssen.
Ken Aspeslagh
Laut dem Dokument developer developer.apple.com/library/mac/#qa/qa1490/_index.html sollten wir das Flag -all_load oder -force_load verwenden. Wie bereits erwähnt, hat der Linker einen Fehler in der 64-Bit-Mac-App und der iPhone-App. "Wichtig: Für 64-Bit- und iPhone-Betriebssystemanwendungen gibt es einen Linker-Fehler, der verhindert, dass -ObjC Objektdateien aus statischen Bibliotheken lädt, die nur Kategorien und keine Klassen enthalten. Die Problemumgehung besteht darin, die Flags -all_load oder -force_load zu verwenden."
Robin
2
@ Ken Aspelagh: Danke, ich hatte das gleiche Problem. Die Flags -ObjC und -all_load müssen der App selbst hinzugefügt werden , nicht der Bibliothek.
titaniumdecoy
3
Tolle Antwort, obwohl Neulinge auf diese Frage achten sollten, dass sie jetzt veraltet ist. Schauen Sie sich die Antwort von tonklon an stackoverflow.com/a/9224606/322748 (all_load / force_load werden nicht mehr benötigt)
Jay Peyer
Ich habe mich fast eine halbe Stunde lang mit diesen Dingen beschäftigt und mit Versuch und Irrtum habe ich es einfach geschafft. Trotzdem danke. Diese Antwort ist +1 wert und das hast du !!!
Deepukjayan
118

Die Antwort von Vladimir ist eigentlich ziemlich gut, aber ich möchte hier etwas mehr Hintergrundwissen geben. Vielleicht findet eines Tages jemand meine Antwort und findet sie hilfreich.

Der Compiler wandelt Quelldateien (.c, .cc, .cpp, .m) in Objektdateien (.o) um. Es gibt eine Objektdatei pro Quelldatei. Objektdateien enthalten Symbole, Code und Daten. Objektdateien können vom Betriebssystem nicht direkt verwendet werden.

Beim Erstellen einer dynamischen Bibliothek (.dylib), eines Frameworks, eines ladbaren Bundles (.bundle) oder einer ausführbaren Binärdatei werden diese Objektdateien vom Linker miteinander verknüpft, um etwas zu erzeugen, das das Betriebssystem als "verwendbar" erachtet, z. B. etwas, das es kann direkt in eine bestimmte Speicheradresse laden.

Beim Erstellen einer statischen Bibliothek werden jedoch alle diese Objektdateien einfach zu einer großen Archivdatei hinzugefügt, daher die Erweiterung der statischen Bibliotheken (.a für Archiv). Eine .a-Datei ist also nichts anderes als ein Archiv von Objektdateien (.o). Stellen Sie sich ein TAR-Archiv oder ein ZIP-Archiv ohne Komprimierung vor. Es ist einfach einfacher, eine einzelne .a-Datei zu kopieren als eine ganze Reihe von .o-Dateien (ähnlich wie bei Java, wo Sie .class-Dateien zur einfachen Verteilung in ein .jar-Archiv packen).

Beim Verknüpfen einer Binärdatei mit einer statischen Bibliothek (= Archiv) erhält der Linker eine Tabelle aller Symbole im Archiv und überprüft, auf welche dieser Symbole die Binärdateien verweisen. Nur die Objektdateien, auf die verwiesen wird, werden vom Linker tatsächlich geladen und vom Verknüpfungsprozess berücksichtigt. Wenn Ihr Archiv beispielsweise 50 Objektdateien enthält, aber nur 20 Symbole enthalten, die von der Binärdatei verwendet werden, werden nur die 20 vom Linker geladen, die anderen 30 werden beim Verknüpfungsprozess vollständig ignoriert.

Dies funktioniert recht gut für C- und C ++ - Code, da diese Sprachen versuchen, beim Kompilieren so viel wie möglich zu tun (obwohl C ++ auch einige Nur-Laufzeit-Funktionen bietet). Obj-C ist jedoch eine andere Art von Sprache. Obj-C hängt stark von den Laufzeitfunktionen ab, und viele Obj-C-Funktionen sind eigentlich nur Laufzeitfunktionen. Obj-C-Klassen haben tatsächlich Symbole, die mit C-Funktionen oder globalen C-Variablen vergleichbar sind (zumindest in der aktuellen Obj-C-Laufzeit). Ein Linker kann sehen, ob auf eine Klasse verwiesen wird oder nicht, sodass er bestimmen kann, welche Klasse verwendet wird oder nicht. Wenn Sie eine Klasse aus einer Objektdatei in einer statischen Bibliothek verwenden, wird diese Objektdatei vom Linker geladen, da der Linker ein verwendetes Symbol sieht. Kategorien sind nur zur Laufzeit verfügbar. Kategorien sind keine Symbole wie Klassen oder Funktionen. Dies bedeutet auch, dass ein Linker nicht feststellen kann, ob eine Kategorie verwendet wird oder nicht.

Wenn der Linker eine Objektdatei mit Obj-C-Code lädt, sind alle Obj-C-Teile immer Teil der Verknüpfungsstufe. Wenn also eine Objektdatei mit Kategorien geladen wird, weil ein Symbol daraus als "in Verwendung" betrachtet wird (sei es eine Klasse, sei es eine Funktion, sei es eine globale Variable), werden die Kategorien ebenfalls geladen und sind zur Laufzeit verfügbar . Wenn die Objektdatei selbst jedoch nicht geladen wird, sind die darin enthaltenen Kategorien zur Laufzeit nicht verfügbar. Eine Objektdatei, die nur Kategorien enthält, wird niemals geladen, da sie keine Symbole enthält, die der Linker jemals als "in Verwendung" betrachten würde. Und das ist das ganze Problem hier.

Es wurden mehrere Lösungen vorgeschlagen. Nachdem Sie nun wissen, wie dies alles zusammenspielt, werfen wir einen weiteren Blick auf die vorgeschlagene Lösung:

  1. Eine Lösung besteht darin -all_load, dem Linker-Aufruf etwas hinzuzufügen . Was wird diese Linker-Flagge tatsächlich tun? Tatsächlich teilt es dem Linker Folgendes mit: " Alle Objektdateien aller Archive laden, unabhängig davon, ob ein Symbol verwendet wird oder nicht ". Das funktioniert natürlich, kann aber auch ziemlich große Binärdateien erzeugen.

  2. Eine andere Lösung besteht darin -force_load, dem Linker-Aufruf den Pfad zum Archiv hinzuzufügen . Dieses Flag funktioniert genauso -all_load, jedoch nur für das angegebene Archiv. Das wird natürlich auch funktionieren.

  3. Die beliebteste Lösung ist das Hinzufügen -ObjCzum Linker-Aufruf. Was wird diese Linker-Flagge tatsächlich tun? Dieses Flag teilt dem Linker mit, dass alle Objektdateien aus allen Archiven geladen werden sollen, wenn Sie feststellen, dass sie Obj-C-Code enthalten . Und "jeder Obj-C-Code" enthält Kategorien. Dies funktioniert auch und erzwingt nicht das Laden von Objektdateien, die keinen Obj-C-Code enthalten (diese werden immer noch nur bei Bedarf geladen).

  4. Eine andere Lösung ist die ziemlich neue Xcode-Build-Einstellung Perform Single-Object Prelink. Was macht diese Einstellung? Wenn diese Option aktiviert ist, werden alle Objektdateien (denken Sie daran, es gibt eine pro Quelldatei) zu einer einzelnen Objektdatei (die keine echte Verknüpfung darstellt, daher der Name PreLink ) und dieser einzelnen Objektdatei (manchmal auch als "Master-Objekt" bezeichnet) zusammengeführt Datei ") wird dann zum Archiv hinzugefügt. Wenn nun ein Symbol der Master-Objektdatei als verwendet betrachtet wird, wird die gesamte Master-Objektdatei als verwendet betrachtet und somit werden alle Objective-C-Teile davon immer geladen. Und da Klassen normale Symbole sind, reicht es aus, eine einzelne Klasse aus einer solchen statischen Bibliothek zu verwenden, um auch alle Kategorien abzurufen.

  5. Die endgültige Lösung ist der Trick, den Vladimir ganz am Ende seiner Antwort hinzugefügt hat. Platzieren Sie ein " falsches Symbol " in einer Quelldatei, die nur Kategorien deklariert. Wenn Sie zur Laufzeit eine der Kategorien verwenden möchten, stellen Sie sicher, dass Sie zur Kompilierungszeit auf das gefälschte Symbol verweisen , da dies dazu führt, dass die Objektdatei vom Linker und damit auch der gesamte darin enthaltene Obj-C-Code geladen wird. Beispielsweise könnte es sich um eine Funktion mit einem leeren Funktionskörper handeln (die beim Aufruf nichts bewirkt), oder es kann sich um eine globale Variable handeln, auf die zugegriffen wird (z. B. eine globale Variable)inteinmal gelesen oder einmal geschrieben, ist dies ausreichend). Im Gegensatz zu allen anderen oben genannten Lösungen verlagert diese Lösung die Kontrolle darüber, welche Kategorien zur Laufzeit verfügbar sind, auf den kompilierten Code (wenn sie verknüpft und verfügbar sein sollen, greift sie auf das Symbol zu, andernfalls greift sie nicht auf das Symbol zu und der Linker ignoriert sie es).

Das war's Leute.

Oh, warte, es gibt noch eine Sache:
Der Linker hat eine Option namens -dead_strip. Was macht diese Option? Wenn der Linker beschlossen hat, eine Objektdatei zu laden, werden alle Symbole der Objektdatei Teil der verknüpften Binärdatei, unabhängig davon, ob sie verwendet werden oder nicht. Beispiel: Eine Objektdatei enthält 100 Funktionen, aber nur eine davon wird von der Binärdatei verwendet. Alle 100 Funktionen werden weiterhin zur Binärdatei hinzugefügt, da Objektdateien entweder als Ganzes oder gar nicht hinzugefügt werden. Das teilweise Hinzufügen einer Objektdatei wird von Linkern normalerweise nicht unterstützt.

Wenn Sie dem Linker jedoch "Dead Strip" mitteilen, fügt der Linker zuerst alle Objektdateien zur Binärdatei hinzu, löst alle Referenzen auf und durchsucht die Binärdatei schließlich nach Symbolen, die nicht verwendet werden (oder nur von anderen Symbolen verwendet werden, die nicht verwendet werden) verwenden). Alle nicht verwendeten Symbole werden dann im Rahmen der Optimierungsphase entfernt. Im obigen Beispiel werden die 99 nicht verwendeten Funktionen wieder entfernt. Dies ist sehr nützlich , wenn Sie Optionen verwenden , wie -load_all, -force_loadoder Perform Single-Object Prelinkweil diese Optionen können leicht binäre Größen dramatisch in einigen Fällen und die Toten Strippen wieder nicht verwendeten Code und Daten entfernen sprengen.

Dead Stripping funktioniert sehr gut für C-Code (z. B. werden nicht verwendete Funktionen, Variablen und Konstanten wie erwartet entfernt) und funktioniert auch für C ++ recht gut (z. B. werden nicht verwendete Klassen entfernt). Es ist nicht perfekt, in einigen Fällen werden einige Symbole nicht entfernt, obwohl es in Ordnung wäre, sie zu entfernen, aber in den meisten Fällen funktioniert es für diese Sprachen recht gut.

Was ist mit Obj-C? Vergiss es! Für Obj-C gibt es kein totes Strippen. Da Obj-C eine Laufzeit-Feature-Sprache ist, kann der Compiler zur Kompilierungszeit nicht sagen, ob ein Symbol wirklich verwendet wird oder nicht. ZB wird eine Obj-C-Klasse nicht verwendet, wenn kein Code vorhanden ist, der direkt darauf verweist, richtig? Falsch! Sie können dynamisch eine Zeichenfolge erstellen, die einen Klassennamen enthält, einen Klassenzeiger für diesen Namen anfordern und die Klasse dynamisch zuweisen. ZB statt

MyCoolClass * mcc = [[MyCoolClass alloc] init];

Ich könnte auch schreiben

NSString * cname = @"CoolClass";
NSString * cnameFull = [NSString stringWithFormat:@"My%@", cname];
Class mmcClass = NSClassFromString(cnameFull);
id mmc = [[mmcClass alloc] init];

In beiden Fällen mmchandelt es sich um einen Verweis auf ein Objekt der Klasse "MyCoolClass", im zweiten Codebeispiel gibt es jedoch keinen direkten Verweis auf diese Klasse (nicht einmal den Klassennamen als statische Zeichenfolge). Alles passiert nur zur Laufzeit. Und das ist , obwohl Klassen sind tatsächlich echte Symbole. Für Kategorien ist es noch schlimmer, da es sich nicht einmal um echte Symbole handelt.

Wenn Sie also eine statische Bibliothek mit Hunderten von Objekten haben, die meisten Ihrer Binärdateien jedoch nur wenige benötigen, ziehen Sie es möglicherweise vor, die obigen Lösungen (1) bis (4) nicht zu verwenden. Andernfalls erhalten Sie sehr große Binärdateien, die alle diese Klassen enthalten, obwohl die meisten von ihnen nie verwendet werden. Für Klassen benötigen Sie normalerweise überhaupt keine spezielle Lösung, da Klassen echte Symbole haben. Solange Sie diese direkt referenzieren (nicht wie im zweiten Codebeispiel), erkennt der Linker ihre Verwendung ziemlich gut. Berücksichtigen Sie für Kategorien jedoch Lösung (5), da nur die Kategorien berücksichtigt werden können, die Sie wirklich benötigen.

Wenn Sie beispielsweise eine Kategorie für NSData möchten, z. B. eine Komprimierungs- / Dekomprimierungsmethode hinzufügen, erstellen Sie eine Header-Datei:

// NSData+Compress.h
@interface NSData (Compression)
    - (NSData *)compressedData;
    - (NSData *)decompressedData;
@end

void import_NSData_Compression ( );

und eine Implementierungsdatei

// NSData+Compress
@implementation NSData (Compression)
    - (NSData *)compressedData 
    {
        // ... magic ...
    }

    - (NSData *)decompressedData
    {
        // ... magic ...
    }
@end

void import_NSData_Compression ( ) { }

Stellen Sie jetzt sicher, dass irgendwo in Ihrem Code import_NSData_Compression()aufgerufen wird. Es spielt keine Rolle, wo es aufgerufen wird oder wie oft es aufgerufen wird. Eigentlich muss es gar nicht aufgerufen werden, es reicht, wenn der Linker das glaubt. Sie können beispielsweise den folgenden Code an einer beliebigen Stelle in Ihrem Projekt einfügen:

__attribute__((used)) static void importCategories ()
{
    import_NSData_Compression();
    // add more import calls here
}

Sie müssen importCategories()Ihren Code nie aufrufen , das Attribut lässt den Compiler und Linker glauben, dass er aufgerufen wird, auch wenn dies nicht der Fall ist.

Und noch ein letzter Tipp:
Wenn Sie -whyloaddem letzten Linkaufruf hinzufügen , druckt der Linker im Build-Protokoll, welche Objektdatei aus welcher Bibliothek aufgrund des verwendeten Symbols geladen wurde. Es wird nur das erste Symbol gedruckt, das als verwendet betrachtet wird. Dies ist jedoch nicht unbedingt das einzige Symbol, das für diese Objektdatei verwendet wird.

Mecki
quelle
1
Vielen Dank für die Erwähnung -whyload, der Versuch zu debuggen, warum der Linker etwas tut, kann ziemlich schwierig sein!
Ben S
Es gibt eine Option Dead Code Strippingin Build Settings>Linking. Ist es dasselbe wie -dead_striphinzugefügt Other Linker Flags?
Xiao
1
@ Sean Ja, es ist das gleiche. Lesen Sie einfach die "Schnelle Hilfe", die für jede Build-Einstellung vorhanden ist. Die Antwort ist genau dort: postimg.org/image/n7megftnr/full
Mecki
@Mecki Danke. Ich habe versucht, loszuwerden -ObjC, also habe ich versucht, Ihren Hack, aber es beschwert sich "import_NSString_jsonObject()", referenced from: importCategories() in main.o ld: symbol(s) not found. Ich habe import_NSString_jsonObjectin meinem Embedded - Framework genannt Utility, und fügen Sie #import <Utility/Utility.h>mit __attribute__am Ende meiner Aussage AppDelegate.h.
Xiao
@Sean Wenn der Linker das Symbol nicht finden kann, verknüpfen Sie nicht mit der statischen Bibliothek, die das Symbol enthält. Nur das Importieren einer ah-Datei aus einem Framework führt nicht zu einer Xcode-Verknüpfung mit dem Framework. Das Framework muss in der Phase der Erstellung mit Frameworks explizit verknüpft werden. Möglicherweise möchten Sie eine eigene Frage für Ihr Verknüpfungsproblem öffnen. Die Beantwortung von Kommentaren ist umständlich und Sie können auch keine Informationen wie die Ausgabe des Erstellungsprotokolls bereitstellen.
Mecki
24

Dieses Problem wurde in LLVM behoben . Der Fix wird als Teil von LLVM 2.9 ausgeliefert. Die erste Xcode-Version, die den Fix enthält, ist Xcode 4.2, der mit LLVM 3.0 ausgeliefert wird. Die Verwendung von -all_loadoder -force_loadwird bei der Arbeit mit XCode 4.2 nicht mehr -ObjC benötigt.

Tonklon
quelle
Bist du dir da sicher? Ich arbeite an einem iOS-Projekt mit Xcode 4.3.2 und kompiliere mit LLVM 3.1. Dies war immer noch ein Problem für mich.
Ashley Mills
Ok, das war etwas ungenau. Die -ObjCFlagge wird noch benötigt und wird es immer sein. Die Problemumgehung war die Verwendung von -all_loadoder -force_load. Und das wird nicht mehr benötigt. Ich habe meine Antwort oben korrigiert.
Tonklon
Gibt es einen Nachteil beim Einfügen des Flags -all_load (auch wenn dies nicht erforderlich ist)? Beeinflusst es in irgendeiner Weise die Kompilierungs- / Startzeit?
ZS
Ich arbeite mit Xcode Version 4.5 (4G182) und das Flag -ObjC verschiebt meinen nicht erkannten Auswahlfehler aus der Abhängigkeit von Drittanbietern, die ich verwenden möchte, in die Tiefen der Objective C-Laufzeit: "- [__ NSArrayM-Map :]: nicht erkannter Selektor an Instanz gesendet ... ". Irgendwelche Hinweise?
Robert Atkins
16

Folgendes müssen Sie tun, um dieses Problem beim Kompilieren Ihrer statischen Bibliothek vollständig zu beheben:

Gehen Sie entweder zu Xcode Build Settings und setzen Sie Perform Single-Object Prelink auf YES oder GENERATE_MASTER_OBJECT_FILE = YESin Ihrer Build-Konfigurationsdatei.

Standardmäßig generiert der Linker für jede .m-Datei eine .o-Datei. Kategorien erhalten also unterschiedliche .o-Dateien. Wenn der Linker eine statische Bibliothek .o-Dateien betrachtet, erstellt er nicht einen Index aller Symbole pro Klasse (Laufzeit wird, egal was).

Diese Anweisung fordert den Linker auf, alle Objekte in einer großen .o-Datei zusammenzufassen. Dadurch wird der Linker, der die statische Bibliothek verarbeitet, gezwungen, alle Klassenkategorien zu indizieren.

Hoffe das klärt es.

Amosel
quelle
Dies hat es für mich behoben, ohne dass -ObjC zum Verknüpfungsziel hinzugefügt werden musste.
Matthew Crenshaw
Nach dem Update auf die neueste Version der BlocksKit- Bibliothek musste ich diese Einstellung verwenden, um das Problem zu beheben (ich habe bereits das Flag -ObjC verwendet, aber das Problem wurde immer noch angezeigt).
Rakmoh
1
Eigentlich ist deine Antwort nicht ganz richtig. Ich fordere den Linker nicht auf, alle Kategorien derselben Klasse in einer .o-Datei zusammenzufassen, sondern den Linker, alle Objektdateien (.o) in einer einzigen großen Objektdatei zu verknüpfen, bevor eine statische Bibliothek erstellt wird sie / es. Sobald auf ein Symbol aus der Bibliothek verwiesen wird, werden alle Symbole geladen. Dies funktioniert jedoch nicht, wenn kein Symbol referenziert wird (z. B. wenn es nicht funktioniert, wenn nur Kategorien in der Bibliothek vorhanden sind).
Mecki
Ich denke nicht, dass dies funktionieren wird, wenn Sie vorhandenen Klassen wie NSData Kategorien hinzufügen.
Bob Whiteman
Auch ich habe Probleme beim Hinzufügen von Kategorien zu vorhandenen Klassen. Mein Plugin kann sie zur Laufzeit nicht erkennen.
David Dunham
9

Ein Faktor, der selten erwähnt wird, wenn die Diskussion über die Verknüpfung statischer Bibliotheken auftaucht, ist die Tatsache, dass Sie auch die Kategorien selbst in die Erstellungsphasen aufnehmen müssen -> Dateien kopieren und Quellen der statischen Bibliothek selbst kompilieren müssen .

Apple betont diese Tatsache auch nicht in seinen kürzlich veröffentlichten " Using Static Libraries" in iOS .

Ich habe einen ganzen Tag damit verbracht, alle möglichen Variationen von -objC und -all_load usw. auszuprobieren. Aber es kam nichts dabei heraus. Diese Frage machte mich auf dieses Problem aufmerksam. (Versteh mich nicht falsch. Du musst immer noch das -objC-Zeug machen. Aber es ist mehr als nur das.)

Eine weitere Aktion, die mir immer geholfen hat, ist, dass ich die enthaltene statische Bibliothek immer zuerst selbst erstelle. Dann erstelle ich die umschließende Anwendung.

Abbood
quelle
-1

Sie müssen wahrscheinlich die Kategorie im "öffentlichen" Header Ihrer statischen Bibliothek haben: #import "MyStaticLib.h"

christo16
quelle