Meilleures pratiques iOS Prefix.pch

90

J'ai vu de nombreux développeurs ajouter diverses macros pratiques au Prefix.pch de leurs projets iOS.

Que recommandez-vous (ou non) d'ajouter au fichier iOS Prefix.pch? À quoi ressemble votre Prefix.pch?

hpique
la source
Un article de blog détaillé sur le sujet: cimgf.com/2010/05/02/my-current-prefix-pch-file
hpique
2
Mettez simplement vos macros dans un fichier d'en-tête, par exemple Macros.h, puis importez ce fichier dans votre fichier prefix.pch.
Malloc
Je suis également confronté au même problème ... comment résoudre dans Xcode 6.1
Yalamandarao

Réponses:

121

Ewww… ne mettez pas de macros dans un fichier .pch! Un fichier .pch est, par définition, un en-tête précompilé spécifique au projet. Il ne doit vraiment pas être utilisé au-delà du contexte du projet et il ne doit vraiment contenir que des #includes et des #imports.

Si vous avez des macros et que vous souhaitez partager entre les en-têtes, alors collez-les dans un fichier d'en-tête qui leur est propre - Common.hou autre - et #include cela au début du .pch.

bbum
la source
Qu'inclueriez-vous dans ce Common.h?
hpique
4
Rien; Je ne mettrais que les divers #defines, etc ... dedans.
bbum
37

Pour iOS et OS X modernes, les utilisateurs devraient utiliser des modules . Ceci est activé par défaut pour les nouveaux projets et l'importation / inclusion est effectuée à l'aide de @import.

Les modules permettent au compilateur de créer une représentation intermédiaire du contenu d'un module (par exemple les en-têtes d'un framework). Tout comme un PCH, cette représentation intermédiaire peut être partagée entre plusieurs traductions. Mais les modules vont encore plus loin car un module n'est pas nécessairement spécifique à une cible, et leurs déclarations n'ont pas besoin d'être localisées (en a *.pch). Cette représentation peut vous épargner une tonne de travail de compilateur redondant.

En utilisant des modules, vous n'avez pas besoin d'un PCH, et vous devriez probablement les supprimer complètement - en faveur de l'utilisation de @importlocal à la dépendance. Dans ce cas, un PCH vous évite seulement de taper inclusions locales dans les dépendances (ce que l'OMI vous devriez faire de toute façon).

Maintenant, si nous revenons à la question originale: vous devriez éviter de remplir votre PCH avec toutes sortes de choses aléatoires; Macros, constantes #defineset toutes sortes de petites bibliothèques. En règle générale, vous devez omettre ce qui est vraiment inutile pour la majorité de vos fichiers source . Mettre toutes sortes de choses dans votre PCH ne fait qu'ajouter beaucoup de poids et de dépendance. Je vois des gens mettre tout ce qu'ils relient et plus encore dans le PCH. En réalité, les frameworks auxiliaires ne doivent généralement être visibles que par quelques traductions dans la plupart des cas. Par exemple: "Voici nos trucs StoreKit - importons StoreKit uniquement là où il doitÊtre visible. Plus précisément, ces 3 traductions ". Cela réduit vos temps de construction et vous aide à garder une trace de vos dépendances, afin que vous puissiez réutiliser le code plus facilement. Donc, dans un projet ObjC, vous vous arrêterez généralement à Foundation. S'il y en a beaucoup de l'interface utilisateur, alors vous pouvez envisager d'ajouter UIKit ou AppKit à votre PCH. Tout cela suppose que vous souhaitiez optimiser les temps de construction. L'un des problèmes avec les PCH volumineux qui incluent (presque) tout est que la suppression des dépendances inutiles prend beaucoup de temps. Une fois les dépendances de votre projet augmentent et vos temps de construction augmentent, vous devez riposter en éliminant les dépendances inutiles afin de réduire vos temps de construction. De plus, tout ce qui change souvent doit généralement être conservé hors de votre PCH. Un changement nécessite une reconstruction complète. Il existe quelques options pour partager des PCH. Si vous utilisez des PCH,

En ce qui concerne ce que j'ai mis dans mon PCH: j'ai arrêté de les utiliser pour la grande majorité des cibles il y a des années. Il n'y a généralement pas assez de points communs pour se qualifier. Gardez à l'esprit que j'écris C ++, ObjC, ObjC ++ et C - le compilateur en émet un pour chaque langue de votre cible. Ainsi, leur activation entraînait souvent des temps de compilation plus lents et des E / S plus élevées. En fin de compte, augmenter la dépendance n'est pas un bon moyen de lutter contre la dépendance dans des projets complexes. En travaillant avec plusieurs langues / dialectes, il existe de nombreuses variations dans les dépendances requises pour une cible donnée. Non, je ne conseillerais pas cela comme optimal pour chaque projet, mais cela donne une certaine perspective à la gestion des dépendances dans les grands projets.


Références


Remarques

  • Cette question a été posée à l'origine quelques années avant l'introduction des modules.
  • Actuellement (Xcode 5.0), les modules fonctionnent pour C et ObjC, mais pas pour C ++.
Justin
la source
Qu'est-ce que cela signifie par reconstruction complète pour un projet avec module activé. Vous savez que ce nouvel en-tête de pontage -Swift.h n'est certainement pas un bon candidat pour un .pch. Mais j'ai vu des gens faire ça. Donc, comme vous pouvez voir si quelqu'un fait cela. Nous avons un en-tête toujours changeant en .pch. Donc, il reconstruit tout dans le fichier .pch chaque fois que le -Swift.h est régénéré. Es-tu d'accord avec ça? Vous avez plus d'entrées?
MadNik
@MadNik Supposons que le PCH de votre application inclut un en-tête principal d'une bibliothèque / structure que vous développez activement. Par exemple: #import "MyLib/MyLib.h". Chaque fois qu'un fichier est inclus par des MyLib.hmodifications, chaque fichier source de l'application doit être recompilé. Si vous utilisez MyLib dans un seul fichier source, seul ce fichier doit être recompilé lorsque MyLib change. Croyez-le ou non, je n'utilise aucun fichier PCH ces jours-ci.
justin
8

Je suis d'accord avec bbum. Mon point de vue sur le fichier PCH est qu'il ne devrait contenir à peu près que des déclarations #includeou #import. Donc, si vous avez un tas de macros utiles et de haut niveau, définissez-les dans quelque chose comme Common.het #importce fichier, comme le suggère bbum.

Je vais habituellement un peu plus loin et d' utiliser le fichier de PCH à #importun fichier appelé XXCategories.h(où XXest la convention de préfixe nommage des classes que vous utilisez) qui contient #imports pour tous mes UIKit et catégories classe Fondation: NSString+XXAdditions.h, UIColor+XXAdditons.h, etc.

CIFiltre
la source
Je suis juste curieux. Dans le fichier .PCH, quelle est la différence entre importer un fichier Common.h qui en contient plusieurs #importet simplement les importer #importdirectement? Ne serait-ce pas la même chose? Ou cela affecte-t-il les performances?
Hlung
À ma connaissance, il n'y a pas de réelle différence. C'est plus une pratique exemplaire, je suppose. Plutôt que de pousser un tas de macros et d'autres choses dans votre fichier PCH, cela ne devrait être que pour #importet #include.
CIFilter
1
La différence est la réutilisation. Le PCH est spécifique au projet. Le Common.h serait commun à de nombreux projets. Cela revient à demander pourquoi vous ne mettez pas simplement toutes vos classes utilitaires dans votre projet au lieu de créer un sous-projet que vous pouvez réutiliser. Bien que ce soit un exemple artificiel, car ce n'est qu'un simple copier-coller ... mais le copier-coller est méchant.
bandejapaisa
6

créer un fichier d'en-tête "macros.h"

importer cet en-tête dans Prefix.pch

Dans ce macros.h mettez tous les cadres et autres choses importantes.

Si vous êtes préoccupé par les performances, ne vous inquiétez pas, regardez ce que dit Apple:

En-têtes et performances

Si vous craignez que l'inclusion d'un fichier d'en-tête principal ne fasse gonfler votre programme, ne vous inquiétez pas. Étant donné que les interfaces OS X sont implémentées à l'aide de frameworks, le code de ces interfaces réside dans une bibliothèque partagée dynamique et non dans votre exécutable. De plus, seul le code utilisé par votre programme est chargé en mémoire lors de l'exécution, de sorte que votre encombrement en mémoire reste également petit. Quant à l'inclusion d'un grand nombre de fichiers d'en-tête lors de la compilation, encore une fois, ne vous inquiétez pas. Xcode fournit une fonction d'en-tête précompilée pour accélérer les temps de compilation. En compilant tous les en-têtes du framework à la fois, il n'est pas nécessaire de recompiler les en-têtes sauf si vous ajoutez un nouveau framework. En attendant, vous pouvez utiliser n'importe quelle interface des frameworks inclus avec peu ou pas de pénalité de performances.

aussi dans mes macros.h j'ai mis beaucoup de constantes comme:

// 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
la source
2
Un autre utile ici: #define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ )... pour exécuter des blocs sur le thread principal:async(^{ self.someLabel.text = @":D"; });
Alejandro Iván