Práticas recomendadas para iOS Prefix.pch

90

Tenho visto muitos desenvolvedores que adicionam várias macros de conveniência ao Prefix.pch de seus projetos iOS.

O que você recomenda (ou não) adicionar ao arquivo iOS Prefix.pch? Qual é a aparência do seu Prefix.pch?

hpique
fonte
Uma postagem de blog detalhada sobre o assunto: cimgf.com/2010/05/02/my-current-prefix-pch-file
hpique
2
Simplesmente coloque suas macros em um arquivo de cabeçalho, por exemplo Macros.h, e importe esse arquivo para o seu prefix.pch.
Malloc
Também estou enfrentando o mesmo problema ... como resolver no Xcode 6.1
Yalamandarao

Respostas:

121

Ewww… não coloque macros em um arquivo .pch! Um arquivo .pch é, por definição, um cabeçalho pré-compilado específico do projeto. Realmente não deve ser usado fora do contexto do projeto e realmente não deve conter nada além de #includes e #imports.

Se você tiver algumas macros e outras que deseja compartilhar entre os cabeçalhos, coloque-as em um arquivo de cabeçalho próprio - Common.hou qualquer outra coisa - e #include isso no início do .pch.

bbum
fonte
O que você incluiria nesse Common.h?
hpique
4
Nada; Eu apenas colocaria os vários #defines, etc ... nele.
bbum
37

Para iOS e OS X modernos, as pessoas deveriam usar Módulos . Isso é habilitado por padrão para novos projetos, e a importação / inclusão é realizada usando@import .

Os módulos permitem que o compilador crie uma representação intermediária do conteúdo de um módulo (por exemplo, os cabeçalhos de um framework). Muito parecido com um PCH, esta representação intermediária pode ser compartilhada entre várias traduções. Mas os módulos vão além porque um módulo não é necessariamente específico de destino e suas declarações não precisam ser localizadas (para a *.pch). Esta representação pode economizar uma tonelada de trabalho de compilador redundante.

Usando módulos, você não precisa de um PCH e provavelmente deveria simplesmente eliminá-los completamente - em favor de usar o @importlocal para a dependência. Nesse caso, um PCH está apenas evitando que você digite inclusões locais para dependências (o que, IMO, você deve fazer de qualquer maneira).

Agora, se olharmos para trás para a pergunta original: você deve evitar encher seu PCH com todo tipo de coisas aleatórias; Macros, constantes #definese todos os tipos de pequenas bibliotecas. Geralmente, você deve omitir o que realmente é desnecessário para a maioria dos arquivos de origem . Colocar todos os tipos de coisas em seu PCH é apenas adicionar um monte de peso e dependência. Vejo que as pessoas colocam tudo o que têm link e mais no PCH. Na realidade, estruturas auxiliares normalmente só precisam ser visíveis para algumas traduções na maioria dos casos. Por exemplo, "Aqui está o nosso material do StoreKit - vamos importar o StoreKit apenas para onde ele deveSeja visível. Especificamente, essas 3 traduções ". Isso mantém seus tempos de compilação baixos e ajuda a controlar suas dependências, para que você possa reutilizar o código mais facilmente. Portanto, em um projeto ObjC, você normalmente pararia na Fundação. Se houver muito de IU, então você pode considerar adicionar UIKit ou AppKit ao seu PCH. Isso tudo pressupõe que você deseja otimizar os tempos de compilação. Um dos problemas com grandes PCHs que incluem (quase) tudo é que a remoção de dependências desnecessárias consome muito tempo. as dependências do seu projeto aumentam e os tempos de construção aumentam, você precisa revidar eliminando dependências desnecessárias para reduzir os tempos de construção. Além disso, qualquer coisa que mude com frequência geralmente deve ser mantida fora do seu PCH. Uma mudança requer uma reconstrução completa. Existem algumas opções para compartilhar PCHs. Se você usar PCHs,

Quanto ao que coloquei no meu PCH: parei de usá-los para a grande maioria dos alvos anos atrás. Geralmente, não há o suficiente em comum para se qualificar. Tenha em mente que eu escrevo C ++, ObjC, ObjC ++ e C - o compilador emite um para cada idioma em seu destino. Portanto, habilitá-los geralmente resultava em tempos de compilação mais lentos e maior I / O. Em última análise, aumentar a dependência não é uma boa maneira de combater a dependência em projetos complexos. Trabalhando com vários idiomas / dialetos, há muitas variações nas dependências necessárias para um determinado destino. Não, eu não recomendaria isso como ideal para todos os projetos, mas isso dá alguma perspectiva para o gerenciamento de dependências em projetos maiores.


Referências


Notas

  • Esta pergunta foi feita originalmente alguns anos antes da introdução dos Módulos.
  • Atualmente (Xcode 5.0), os módulos funcionam para C e ObjC, mas não para C ++.
justin
fonte
O que significa reconstrução completa para um projeto habilitado por módulo. Você sabe que este novo cabeçalho de ponte -Swift.h definitivamente não é um candidato certo para um .pch. Mas tenho visto pessoas fazerem isso. Então, como você pode ver se alguém faz isso. Temos um cabeçalho sempre mutável em .pch. Portanto, ele reconstrói tudo no arquivo .pch sempre que o -Swift.h é regenerado. Você concorda com isso? Você tem mais entradas?
MadNik
@MadNik Suponha que o PCH do seu aplicativo inclua um cabeçalho mestre de uma biblioteca / estrutura que você está desenvolvendo ativamente. Por exemplo: #import "MyLib/MyLib.h". Sempre que um arquivo incluído por MyLib.halterações, todos os arquivos de origem no aplicativo devem ser recompilados. Se você usar o MyLib em apenas um arquivo de origem, apenas esse arquivo deverá ser recompilado quando o MyLib for alterado. Acredite ou não, não estou usando nenhum arquivo PCH atualmente.
justin
8

Eu concordo com o bbum. Minha opinião sobre o arquivo PCH é que ele deve conter praticamente apenas instruções #includeou #import. Então se você tem um monte de votos, macros de alto nível, defini-los em algo como Common.he #importesse arquivo, como bbum sugeriu.

Eu costumo dar um passo adiante e usar o arquivo PCH para #importum arquivo chamado XXCategories.h(onde XXé a classe convenção de nomenclatura prefixo que você usa) que contém #imports para todas as categorias minha UIKit e de classe Foundation: NSString+XXAdditions.h, UIColor+XXAdditons.h, etc.

CIFilter
fonte
Eu só estou curioso. No arquivo .PCH, qual é a diferença entre importar um Common.h que tem vários #importe apenas importá-los #importdiretamente? Não seriam iguais? Ou isso afeta o desempenho?
Hlung
Até onde sei, não há diferença real . É mais uma prática recomendada, eu acho. Em vez de enfiar um monte de macros e outras coisas em seu arquivo PCH, deve ser apenas para #importe #include.
CIFilter
1
A diferença é a reutilização. O PCH é específico do projeto. O Common.h seria comum a muitos projetos. É o mesmo que perguntar por que você simplesmente não coloca todas as classes util em seu projeto em vez de criar um subprojeto que pode ser reutilizado. Embora seja um exemplo artificial, porque é apenas um simples copiar e colar ... mas copiar e colar é impertinente.
bandejapaisa
6

crie um arquivo de cabeçalho "macros.h"

importe este cabeçalho para Prefix.pch

Neste macros.h coloque todas as estruturas e outras coisas importantes.

Se você está preocupado com o desempenho, não se preocupe, veja o que a apple diz:

Cabeçalhos e desempenho

Se você está preocupado com o fato de que incluir um arquivo de cabeçalho mestre pode fazer com que seu programa inche, não se preocupe. Como as interfaces do OS X são implementadas usando estruturas, o código para essas interfaces reside em uma biblioteca compartilhada dinâmica e não em seu executável. Além disso, apenas o código usado por seu programa é carregado na memória em tempo de execução, portanto, seu espaço na memória permanece pequeno. Quanto a incluir um grande número de arquivos de cabeçalho durante a compilação, mais uma vez, não se preocupe. O Xcode fornece um recurso de cabeçalho pré-compilado para acelerar os tempos de compilação. Compilando todos os cabeçalhos da estrutura de uma vez, não há necessidade de recompilar os cabeçalhos, a menos que você adicione uma nova estrutura. Enquanto isso, você pode usar qualquer interface das estruturas incluídas com pouca ou nenhuma penalidade de desempenho.

também em minhas macros.h coloquei várias 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
fonte
2
Outro útil aqui: #define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ )... para executar blocos no tópico principal:async(^{ self.someLabel.text = @":D"; });
Alejandro Iván