Best Practices für iOS Prefix.pch

90

Ich habe viele Entwickler gesehen, die der Prefix.pch ihrer iOS-Projekte verschiedene Komfortmakros hinzufügen.

Was empfehlen Sie (oder nicht), um es zur Datei iOS Prefix.pch hinzuzufügen? Wie sieht Ihre Prefix.pch aus?

hpique
quelle
Ein ausführlicher Blog-Beitrag zu diesem Thema: cimgf.com/2010/05/02/my-current-prefix-pch-file
hpique
2
Fügen Sie einfach Ihre Makros in eine Header-Datei ein Macros.hund importieren Sie diese Datei in Ihre prefix.pch.
Malloc
Ich
stehe

Antworten:

121

Ewww… füge keine Makros in eine .pch-Datei ein! Eine .pch-Datei ist per Definition ein projektspezifischer vorkompilierter Header. Es sollte wirklich nicht über den Kontext des Projekts hinaus verwendet werden und es sollte wirklich nichts anderes als #includes und #imports enthalten.

Wenn Sie einige Makros haben und solche, die Sie zwischen Headern teilen möchten, stecken Sie sie in eine eigene Header-Datei - Common.hoder was auch immer - und #include das am Anfang der .pch.

bbum
quelle
Was würden Sie in diese Common.h aufnehmen?
Hpique
4
Nichts; Ich würde nur die verschiedenen #defines usw. einfügen.
bbum
37

Für modernes iOS und OS X sollten Benutzer Module verwenden . Dies ist standardmäßig für neue Projekte aktiviert, und der Import / Einschluss erfolgt mithilfe von @import.

Mit Modulen kann der Compiler eine Zwischendarstellung des Inhalts eines Moduls erstellen (z. B. die Header eines Frameworks). Ähnlich wie bei einem PCH kann diese Zwischendarstellung von mehreren Übersetzungen gemeinsam genutzt werden. Module gehen jedoch noch einen Schritt weiter, da ein Modul nicht unbedingt zielspezifisch ist und ihre Deklarationen nicht lokalisiert werden müssen (zu a *.pch). Diese Darstellung kann Ihnen eine Menge redundanter Compilerarbeit ersparen.

Wenn Sie Module verwenden, benötigen Sie keinen PCH, und Sie sollten sie wahrscheinlich ganz weglassen - zugunsten der Verwendung von @importlocal für die Abhängigkeit. In diesem Fall erspart Ihnen ein PCH nur die Eingabe lokaler Einschlüsse für Abhängigkeiten (welche IMO Sie sowieso ausführen sollten).

Wenn wir jetzt auf die ursprüngliche Frage zurückblicken: Sie sollten vermeiden, Ihren PCH mit allen möglichen zufälligen Dingen zu füllen. Makros, Konstanten #definesund alle möglichen kleinen Bibliotheken. Im Allgemeinen sollten Sie weglassen, was für die meisten Ihrer Quelldateien wirklich unnötig ist . Wenn Sie alle möglichen Dinge in Ihren PCH aufnehmen, wird nur eine Menge Gewicht und Abhängigkeit hinzugefügt. Ich sehe, dass Leute alles, was sie verknüpfen, und mehr in den PCH stellen. In der Realität müssen Hilfsrahmen in den meisten Fällen nur für wenige Übersetzungen sichtbar sein. ZB "Hier ist unser StoreKit-Zeug - lasst uns StoreKit nur dort importieren, wo es musssei sichtbar. Insbesondere diese 3 Übersetzungen ". Dies hält Ihre Erstellungszeiten niedrig und hilft Ihnen, Ihre Abhängigkeiten im Auge zu behalten, sodass Sie Code leichter wiederverwenden können. In einem ObjC-Projekt würden Sie normalerweise bei Foundation anhalten. Wenn es viel gibt In der Benutzeroberfläche können Sie UIKit oder AppKit zu Ihrem PCH hinzufügen. Dies setzt voraus, dass Sie die Erstellungszeiten optimieren möchten. Eines der Probleme bei großen PCHs, die (fast) alles enthalten, besteht darin, dass das Entfernen unnötiger Abhängigkeiten sehr zeitaufwändig ist Wenn die Abhängigkeiten Ihres Projekts zunehmen und Ihre Erstellungszeiten steigen, müssen Sie sich wehren, indem Sie unnötige Abhängigkeiten beseitigen, um Ihre Erstellungszeiten zu verkürzen. Außerdem sollten alle Änderungen häufig aus Ihrem PCH herausgehalten werden. Eine Änderung erfordert eine vollständige Neuerstellung. Es gibt einige Optionen zum Freigeben von PCHs. Wenn Sie PCHs verwenden,

Was ich in meinen PCH geschrieben habe: Ich habe sie vor Jahren für die überwiegende Mehrheit der Ziele nicht mehr verwendet. Es gibt normalerweise nicht genug Gemeinsamkeiten, um sich zu qualifizieren. Denken Sie daran, ich schreibe C ++, ObjC, ObjC ++ und C - der Compiler gibt eine für jede Sprache in Ihrem Ziel aus. Ihre Aktivierung führte daher häufig zu langsameren Kompilierungszeiten und höheren E / A. Letztendlich ist eine zunehmende Abhängigkeit kein guter Weg, um die Abhängigkeit in komplexen Projekten zu bekämpfen. Bei der Arbeit mit mehreren Sprachen / Dialekten gibt es große Unterschiede in den Abhängigkeiten, die für ein bestimmtes Ziel erforderlich sind. Nein, ich würde nicht empfehlen, dass dies für jedes Projekt optimal ist, aber dies gibt dem Abhängigkeitsmanagement in größeren Projekten eine gewisse Perspektive.


Verweise


Anmerkungen

  • Diese Frage wurde ursprünglich einige Jahre vor der Einführung von Modules gestellt.
  • Derzeit (Xcode 5.0) funktionieren Module für C und ObjC, jedoch nicht für C ++.
Justin
quelle
Was bedeutet es für die vollständige Neuerstellung eines modulfähigen Projekts? Sie wissen, dass dieser neue -Swift.h-Bridging-Header definitiv kein richtiger Kandidat für eine .pch ist. Aber ich habe Leute gesehen, die das getan haben. Wie Sie sehen können, wenn jemand das tut. Wir haben einen sich ständig ändernden Header in .pch. Daher wird jedes Mal, wenn -Swift.h neu generiert wird, alles in der .pch-Datei neu erstellt. Stimmst du dem zu? Sie haben mehr Eingaben?
MadNik
@MadNik Angenommen, der PCH Ihrer App enthält einen Master-Header einer Bibliothek / eines Frameworks, das Sie aktiv entwickeln. Zum Beispiel : #import "MyLib/MyLib.h". MyLib.hJedes Mal, wenn eine Datei Änderungen enthält, muss jede Quelldatei in der App neu kompiliert werden. Wenn Sie MyLib in nur einer Quelldatei verwenden, muss nur diese Datei neu kompiliert werden, wenn sich MyLib ändert. Ob Sie es glauben oder nicht, ich verwende derzeit keine PCH-Dateien.
Justin
8

Ich stimme bbum zu. Mein Nehmen auf der PCH - Datei ist , dass es ziemlich viel nur enthalten soll #includeoder #importAussagen. Also , wenn Sie eine Reihe hilfreicher, High-Level - Makros haben, definieren sie in so etwas wie Common.hund #importdiese Datei, wie BBUM vorgeschlagen.

Normalerweise gehe ich einen Schritt weiter und verwenden Sie die PCH - Datei in #importeine Datei mit dem Namen XXCategories.h(wo XXist die Klassenbenennung Präfix Konvention Sie verwenden) , die enthält #imports für alle meine UIKit und Foundation Klassenkategorien: NSString+XXAdditions.h, UIColor+XXAdditons.husw.

CIFilter
quelle
Ich bin nur Neugierig. Was ist in der .PCH-Datei der Unterschied zwischen dem Importieren einer Common.h mit verschiedenen Funktionen #importund dem #importdirekten Importieren dieser Dateien ? Wären diese nicht gleich? Oder wirkt sich dies auf die Leistung aus?
Hlung
Meines Wissens gibt es keinen wirklichen Unterschied. Es ist eher eine bewährte Methode, denke ich. Anstatt eine Reihe von Makros und anderen Dingen in Ihre PCH-Datei zu verschieben, sollte dies nur für sein#import und sein #include.
CIFilter
1
Der Unterschied liegt in der Wiederverwendbarkeit. Der PCH ist projektspezifisch. Die Common.h wäre vielen Projekten gemeinsam. Es ist vergleichbar mit der Frage, warum Sie nicht einfach alle Ihre util-Klassen in Ihr Projekt einfügen, anstatt ein Unterprojekt zu erstellen, das Sie wiederverwenden können. Obwohl ein erfundenes Beispiel, weil es nur ein einfaches Kopieren und Einfügen ist ... aber Kopieren und Einfügen ist ungezogen.
Bandejapaisa
6

Erstellen Sie eine Header-Datei "macros.h"

Importieren Sie diesen Header in Prefix.pch

In dieses macros.h setzen Sie alle Frameworks und andere wichtige Dinge.

Wenn Sie sich Sorgen um die Leistung machen, machen Sie sich keine Sorgen, schauen Sie, was Apple sagt:

Header und Leistung

Machen Sie sich keine Sorgen, wenn Sie befürchten, dass das Einfügen einer Master-Header-Datei zu einem Aufblähen Ihres Programms führen könnte. Da OS X-Schnittstellen mithilfe von Frameworks implementiert werden, befindet sich der Code für diese Schnittstellen in einer dynamischen gemeinsam genutzten Bibliothek und nicht in Ihrer ausführbaren Datei. Außerdem wird zur Laufzeit immer nur der von Ihrem Programm verwendete Code in den Speicher geladen, sodass Ihr In-Memory-Footprint ebenfalls klein bleibt. Machen Sie sich keine Sorgen, wenn Sie beim Kompilieren eine große Anzahl von Header-Dateien einbeziehen. Xcode bietet eine vorkompilierte Header-Funktion, um die Kompilierungszeiten zu verkürzen. Wenn Sie alle Framework-Header gleichzeitig kompilieren, müssen Sie die Header nur neu kompilieren, wenn Sie ein neues Framework hinzufügen. In der Zwischenzeit können Sie jede Schnittstelle aus den enthaltenen Frameworks ohne oder mit nur geringen Leistungseinbußen verwenden.

auch in meine macros.h habe ich viele Konstanten gesetzt wie:

// delegate
#define UIAppDelegate (AppDelegate *)[[UIApplication sharedApplication] delegate]
#define APPDELEGATE   ((AppDelegate *)[[UIApplication sharedApplication] delegate])

// system
#define IS_IPHONE_4INCH (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone && [UIScreen mainScreen].bounds.size.height==568)
#define IS_IPAD                     (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

// screen size
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0)
#define IS_IPHONE_5 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
#define IS_IPHONE_6 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
#define IS_IPHONE_6PLUS (IS_IPHONE && [[UIScreen mainScreen] nativeScale] == 3.0f)
#define IS_IPHONE_6_PLUS (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0)
#define IS_RETINA ([[UIScreen mainScreen] scale] == 2.0)
#define IS_RETINA_DISPLAY ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))
#define IS_PORTRAIT                 UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])
#define IS_LANDSCAPE                UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])

//system version
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)

// math
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))

// cores
#define RGB(r,g,b)    [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
#define RGBA(r,g,b,a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:a]
#define MAKECOLOR(R, G, B, A) [UIColor colorWithRed:((float)R/255.0f) green:((float)G/255.0f) blue:((float)B/255.0f) alpha:A]
#define MAKECOLORFROMHEX(hexValue) [UIColor colorWithRed: ((float)((hexValue & 0xFF0000) >> 16))/255.0 green:((float)((hexValue & 0xFF00) >> 8))/255.0 blue:((float)(hexValue & 0xFF))/255.0 alpha:1.0]



//customizations
#define SHOW_STATUS_BAR               [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
#define HIDE_STATUS_BAR               [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];

#define SHOW_NAVIGATION_BAR           [self.navigationController setNavigationBarHidden:FALSE];
#define HIDE_NAVIGATION_BAR           [self.navigationController setNavigationBarHidden:TRUE];

#define VC_OBJ(x) [[x alloc] init]
#define VC_OBJ_WITH_NIB(x) [[x alloc] initWithNibName : (NSString *)CFSTR(#x) bundle : nil]

#define RESIGN_KEYBOARD [[[UIApplication sharedApplication] keyWindow] endEditing:YES];

#define CLEAR_NOTIFICATION_BADGE                       [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
#define REGISTER_APPLICATION_FOR_NOTIFICATION_SERVICE  [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]

#define HIDE_NETWORK_ACTIVITY_INDICATOR                 [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
#define SHOW_NETWORK_ACTIVITY_INDICATOR                 [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
Leo Cavalcante
quelle
2
Ein weiterer nützlicher hier: #define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ )... um Blöcke auf dem Haupt-Thread auszuführen:async(^{ self.someLabel.text = @":D"; });
Alejandro Iván