Mejores prácticas de Prefix.pch de iOS

90

He visto a muchos desarrolladores que agregan varias macros de conveniencia al Prefix.pch de sus proyectos de iOS.

¿Qué recomienda (o no) agregar al archivo Prefix.pch de iOS? ¿Cómo se ve su Prefix.pch?

hpique
fuente
Una publicación de blog detallada sobre el tema: cimgf.com/2010/05/02/my-current-prefix-pch-file
hpique
2
Simplemente coloque sus macros en un archivo de encabezado, por ejemplo Macros.h, y luego importe este archivo a su prefix.pch.
Malloc
También estoy enfrentando el mismo problema ... cómo resolver en Xcode 6.1
Yalamandarao

Respuestas:

121

Ewww ... ¡no pongas macros en un archivo .pch! Un archivo .pch es, por definición, un encabezado precompilado específico del proyecto. Realmente no debería usarse más allá del contexto del proyecto y realmente no debería contener nada más que #includes y #imports.

Si tiene algunas macros y desea compartirlas entre encabezados, péguelas en un archivo de encabezado propio, Common.ho lo #include que sea, y eso al principio del .pch.

bbum
fuente
¿Qué incluirías en ese Common.h?
hpique
4
Nada; Solo pondría las distintas #definiciones, etc ... en él.
bbum
37

Para iOS y OS X modernos, la gente debería usar módulos . Esto está habilitado de forma predeterminada para nuevos proyectos, y la importación / inclusión se realiza mediante @import.

Los módulos permiten al compilador crear una representación intermedia del contenido de un módulo (por ejemplo, los encabezados de un marco). Al igual que un PCH, esta representación intermedia se puede compartir entre varias traducciones. Pero los módulos llevan esto un paso más allá porque un módulo no es necesariamente un objetivo específico y sus declaraciones no necesitan ser localizadas (a *.pch). Esta representación puede ahorrarle mucho trabajo de compilación redundante.

Al usar módulos, no necesita un PCH, y probablemente debería eliminarlos por completo, a favor de usar @importlocal para la dependencia. En ese caso, un PCH solo le evita escribir inclusiones locales a dependencias (lo que en mi opinión debería hacer de todos modos).

Ahora, si miramos hacia atrás a la pregunta original: debe evitar llenar su PCH con todo tipo de cosas aleatorias; Macros, constantes #definesy todo tipo de pequeñas bibliotecas. Generalmente, debe omitir lo que realmente es innecesario para la mayoría de sus archivos fuente . Poner todo tipo de cosas en su PCH es solo agregar un montón de peso y dependencia. Veo que la gente pone todo lo que enlaza y más en el PCH. En realidad, los marcos auxiliares normalmente solo necesitan ser visibles para unas pocas traducciones en la mayoría de los casos. Por ejemplo, "Aquí está nuestro material de StoreKit: importemos StoreKit solo donde debase visible. Específicamente, estas 3 traducciones ". Esto mantiene los tiempos de compilación bajos y lo ayuda a realizar un seguimiento de sus dependencias, para que pueda reutilizar el código más fácilmente. Por lo tanto, en un proyecto de ObjC, generalmente se detendrá en Foundation. Si hay mucho de UI, entonces podría considerar agregar UIKit o AppKit a su PCH. Todo esto asumiendo que desea optimizar los tiempos de compilación. Uno de los problemas con los PCH grandes que incluyen (casi) todo es que eliminar las dependencias innecesarias lleva mucho tiempo. Una vez las dependencias de su proyecto crecen y los tiempos de compilación aumentan, necesita contraatacar eliminando las dependencias innecesarias para reducir los tiempos de compilación. Además, cualquier cosa que cambie con frecuencia generalmente debe mantenerse fuera de su PCH. Un cambio requiere una reconstrucción completa. Hay algunas opciones para compartir PCH. Si usa PCH,

En cuanto a lo que puse en mi PCH: dejé de usarlos para la gran mayoría de objetivos hace años. Por lo general, no hay suficiente en común para calificar. Tenga en cuenta que escribo C ++, ObjC, ObjC ++ y C: el compilador emite uno para cada idioma de su objetivo. Por lo tanto, habilitarlos a menudo resultó en tiempos de compilación más lentos y mayores E / S. En última instancia, aumentar la dependencia no es una buena forma de luchar contra la dependencia en proyectos complejos. Al trabajar con varios idiomas / dialectos, hay mucha variación en las dependencias necesarias para un objetivo determinado. No, no lo recomendaría como óptimo para cada proyecto, pero eso le da cierta perspectiva a la gestión de la dependencia en proyectos más grandes.


Referencias


Notas

  • Esta pregunta se formuló originalmente unos años antes de la introducción de los módulos.
  • Actualmente (Xcode 5.0), los módulos funcionan para C y ObjC, pero no C ++.
justin
fuente
¿Qué significa reconstrucción completa para un proyecto con módulo habilitado? Usted sabe que este nuevo encabezado puente -Swift.h definitivamente no es un candidato adecuado para un .pch. Pero he visto a gente hacer eso. Entonces, como puede ver si alguien hace eso. Tenemos un encabezado siempre cambiante en .pch. Así que reconstruye todo en el archivo .pch cada vez que se regenera -Swift.h. ¿Estás de acuerdo con esto? ¿Tienes más insumos?
MadNik
@MadNik Suponga que el PCH de su aplicación incluye un encabezado maestro de una biblioteca / marco que está desarrollando activamente. Por ejemplo: #import "MyLib/MyLib.h". Cada vez que un archivo incluido por MyLib.hcambios, todos los archivos fuente de la aplicación deben volver a compilarse. Si usa MyLib en un solo archivo fuente, solo ese archivo debe volver a compilarse cuando MyLib cambie. Lo crea o no, no estoy usando ningún archivo PCH en estos días.
Justin
8

Estoy de acuerdo con bbum. Mi opinión sobre el archivo PCH es que debería contener prácticamente solo declaraciones #includeo #import. Entonces, si tiene un montón de macros útiles de alto nivel , defínelas en algo como Common.hy #importese archivo, como sugirió bbum.

Yo suelo ir un paso más allá y utilizar el archivo PCH a #importun archivo llamado XXCategories.h(donde XXes la clase convención de nombres de prefijo que se use) que contiene #imports para todas las categorías de clase y mi UIKit Fundación: NSString+XXAdditions.h, UIColor+XXAdditons.h, etc.

CIFilter
fuente
Tengo curiosidad. En el archivo .PCH, ¿cuál es la diferencia entre importar un Common.h que tiene varios #importy simplemente importarlos #importdirectamente? ¿No serían iguales ?, ¿o afecta alguna actuación?
Hlung
Que yo sepa, no hay una diferencia real . Es más una buena práctica, supongo. En lugar de introducir un montón de macros y otras cosas en su archivo PCH, solo debería ser para #importy #include.
CIFilter
1
La diferencia es la reutilización. El PCH es específico del proyecto. Common.h sería común a muchos proyectos. Es similar a preguntarse por qué no pone todas sus clases de utilidades en su proyecto en lugar de crear un subproyecto que pueda reutilizar. Aunque es un ejemplo artificial, porque es solo un simple copiar y pegar ... pero copiar y pegar es malo.
bandejapaisa
6

crear un archivo de encabezado "macros.h"

importar este encabezado en Prefix.pch

En este macros.h ponemos todos los frameworks y otras cosas importantes.

Si le preocupa el rendimiento, no se preocupe, mire lo que dice Apple:

Encabezados y rendimiento

Si le preocupa que incluir un archivo de encabezado maestro pueda hacer que su programa se hinche, no se preocupe. Debido a que las interfaces de OS X se implementan mediante marcos, el código de esas interfaces reside en una biblioteca compartida dinámica y no en su ejecutable. Además, solo el código utilizado por su programa se carga en la memoria en tiempo de ejecución, por lo que su huella en la memoria permanece pequeña de manera similar. En cuanto a incluir una gran cantidad de archivos de encabezado durante la compilación, una vez más, no se preocupe. Xcode proporciona una función de encabezado precompilado para acelerar los tiempos de compilación. Al compilar todos los encabezados del marco a la vez, no es necesario volver a compilar los encabezados a menos que agregue un nuevo marco. Mientras tanto, puede utilizar cualquier interfaz de los marcos incluidos con poca o ninguna penalización de rendimiento.

también en mis macros.h puse muchas constantes como:

// 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
fuente
2
Otro útil aquí: #define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ )... para ejecutar bloques en el hilo principal:async(^{ self.someLabel.text = @":D"; });
Alejandro Iván