Najlepsze praktyki iOS Prefix.pch

90

Widziałem wielu programistów, którzy dodali różne wygodne makra do Prefix.pch swoich projektów iOS.

Co warto (a czego nie) polecać dodać do pliku iOS Prefix.pch? Jak wygląda Twój Prefix.pch?

hpique
źródło
Szczegółowy wpis na blogu na ten temat: cimgf.com/2010/05/02/my-current-prefix-pch-file
hpique
2
Po prostu umieść swoje makra, na przykład w pliku nagłówkowym Macros.h, a następnie zaimportuj ten plik do pliku prefix.pch.
Malloc
Mam też ten sam problem ... jak rozwiązać w Xcode 6.1
Yalamandarao

Odpowiedzi:

121

Eee… nie umieszczaj makr w pliku .pch! Plik .pch jest z definicji prekompilowanym nagłówkiem specyficznym dla projektu. Naprawdę nie powinno się go używać poza kontekstem projektu i naprawdę nie powinno zawierać niczego poza #includes i #imports.

Jeśli masz jakieś makra i takie, które chcesz udostępnić między nagłówkami, umieść je we własnym pliku nagłówkowym - Common.hlub czymkolwiek - i #include to na początku pliku .pch.

bbum
źródło
Co byś zawarł w tym Common.h?
hpique
4
Nic; Umieściłbym w nim tylko różne #defines, itp ...
bbum
37

W przypadku nowoczesnych systemów iOS i OS X ludzie powinni używać modułów . Jest to domyślnie włączone dla nowych projektów, a importowanie / włączanie odbywa się za pomocą @import.

Moduły pozwalają kompilatorowi stworzyć pośrednią reprezentację zawartości modułu (np. Nagłówki frameworka). Podobnie jak PCH, ta reprezentacja pośrednia może być współdzielona w wielu tłumaczeniach. Ale moduły idą o krok dalej, ponieważ moduł niekoniecznie jest specyficzny dla celu, a ich deklaracje nie muszą być lokalizowane (do a *.pch). Ta reprezentacja może zaoszczędzić mnóstwo zbędnej pracy kompilatora.

Korzystając z modułów, nie potrzebujesz PCH i prawdopodobnie powinieneś po prostu całkowicie je pozbyć - na korzyść użycia @importlokalnego dla zależności. W takim przypadku PCH chroni cię tylko przed wpisywaniem inkluzji lokalnych w zależnościach (którą IMO i tak powinieneś robić).

Teraz, jeśli spojrzymy wstecz na pierwotne pytanie: Powinieneś unikać wypełniania PCH różnymi rodzajami przypadkowych rzeczy; Makra, stałe #definesi wszelkiego rodzaju małe biblioteki. Generalnie powinieneś pominąć to, co naprawdę jest niepotrzebne dla większości plików źródłowych . Umieszczanie wszelkiego rodzaju rzeczy w PCH to po prostu dodawanie wagi i zależności. Widzę, że ludzie umieszczają wszystko, co łączą, a nawet więcej w PCH. W rzeczywistości ramy pomocnicze w większości przypadków muszą być widoczne tylko dla kilku tłumaczeń. Np. „Oto nasze rzeczy StoreKit - zaimportujmy StoreKit tylko tam, gdzie jest to koniecznebądź widoczny. W szczególności te 3 tłumaczenia ”. To skraca czas kompilacji i pomaga śledzić zależności, dzięki czemu można łatwiej ponownie wykorzystać kod. W projekcie ObjC zwykle zatrzymujesz się na Foundation. Jeśli jest dużo interfejsu użytkownika, możesz rozważyć dodanie UIKit lub AppKit do swojego PCH. Wszystko to przy założeniu, że chcesz zoptymalizować czas kompilacji. Jednym z problemów z dużymi PCH, które zawierają (prawie) wszystko, jest to, że usuwanie niepotrzebnych zależności jest bardzo czasochłonne. Kiedyś. Zależności twojego projektu rosną, a czas kompilacji wydłuża się, musisz walczyć, eliminując niepotrzebne zależności, aby skrócić czas kompilacji. Ponadto wszystko, co często się zmienia, powinno być generalnie wyłączone z PCH. Zmiana wymaga pełnej przebudowy. Istnieje kilka opcji udostępniania PCH. Jeśli używasz PCH,

Jeśli chodzi o to, co umieściłem w moim PCH: przestałem ich używać w większości celów lata temu. Po prostu zwykle nie ma ich wystarczająco dużo, aby się zakwalifikować. Pamiętaj, że piszę C ++, ObjC, ObjC ++ i C - kompilator emituje po jednym dla każdego języka w Twoim celu. Dlatego włączenie ich często skutkowało wolniejszymi czasami kompilacji i wyższymi I / O. Ostatecznie zwiększanie zależności nie jest dobrym sposobem walki z uzależnieniem w złożonych projektach. Pracując z wieloma językami / dialektami, istnieje wiele różnic w zależnościach wymaganych dla danego celu. Nie, nie radziłbym tego jako optymalnego dla każdego projektu, ale to daje pewną perspektywę dla zarządzania zależnościami w większych projektach.


Bibliografia


Uwagi

  • To pytanie zostało pierwotnie zadane kilka lat przed wprowadzeniem modułów.
  • Obecnie (Xcode 5.0) moduły działają dla C i ObjC, ale nie dla C ++.
Justin
źródło
Co to oznacza przez całkowitą przebudowę dla projektu z włączonym modułem. Wiesz, że ten nowy nagłówek mostkujący -Swift.h zdecydowanie nie jest odpowiednim kandydatem na plik .pch. Ale widziałem, jak ludzie to robią. Więc jak widać, czy ktoś to robi. Mamy stale zmieniający się nagłówek w .pch. Więc odbudowuje wszystko w pliku .pch za każdym razem, gdy -Swift.h jest regenerowany. Czy zgadzasz się z tym? Masz więcej wejść?
MadNik
@MadNik Załóżmy, że PCH Twojej aplikacji zawiera główny nagłówek biblioteki / struktury, którą aktywnie rozwijasz. Na przykład: #import "MyLib/MyLib.h". Za każdym razem, gdy plik zostanie uwzględniony w MyLib.hzmianach, każdy plik źródłowy w aplikacji musi zostać ponownie skompilowany. Jeśli używasz MyLib tylko w jednym pliku źródłowym, tylko ten plik musi zostać ponownie skompilowany, gdy zmieni się MyLib. Wierz lub nie, ale obecnie nie używam żadnych plików PCH.
Justin
8

Zgadzam się z bbum. Moje zdanie na temat pliku PCH jest takie, że powinien zawierać tylko #includelub wyłącznie #importoświadczenia. Więc jeśli masz kilka pomocnych makr wysokiego poziomu, zdefiniuj je w czymś takim jak Common.hi #importtym pliku, jak sugerował bbum.

Ja zwykle pójść o krok dalej i użyć pliku PCH do #importpliku o nazwie XXCategories.h(gdzie XXjest klasa konwencji nazewnictwa prefiks użyć), który zawiera #imports dla wszystkich kategorii moja UIKit i klasowych założenia: NSString+XXAdditions.h, UIColor+XXAdditons.h, itd.

CIFilter
źródło
Jestem po prostu ciekawy. W pliku .PCH, jaka jest różnica między importowaniem pliku Common.h, który ma różne, #importa zwykłym importowaniem tych plików #importbezpośrednio? Czy to nie byłoby to samo? Czy ma to wpływ na wydajność?
Hlung
O ile mi wiadomo, nie ma rzeczywistego różnicy. Myślę, że to raczej najlepsza praktyka. Zamiast wpychać kilka makr i innych rzeczy do pliku PCH, powinno to być tylko dla #importi #include.
CIFilter
1
Różnica polega na możliwości ponownego użycia. PCH jest specyficzne dla projektu. Common.h byłby wspólny dla wielu projektów. Jest to podobne do pytania, dlaczego nie umieszczasz po prostu wszystkich swoich klas użytkowych w projekcie, zamiast tworzyć podprojekt, którego możesz ponownie użyć. Chociaż to wymyślony przykład, ponieważ to tylko prosta kopiuj wklej ... ale kopiuj wklej jest niegrzeczny.
bandejapaisa
6

utwórz plik nagłówkowy „macros.h”

zaimportuj ten nagłówek do Prefix.pch

W tym macros.h umieść wszystkie frameworki i inne ważne rzeczy.

Jeśli martwisz się o wydajność, nie martw się, zobacz, co mówi Apple:

Nagłówki i wydajność

Jeśli obawiasz się, że dołączenie głównego pliku nagłówkowego może spowodować rozdęcie programu, nie martw się. Ponieważ interfejsy OS X są implementowane przy użyciu struktur, kod tych interfejsów znajduje się w dynamicznej bibliotece współdzielonej, a nie w pliku wykonywalnym. Ponadto tylko kod używany przez program jest zawsze ładowany do pamięci w czasie wykonywania, więc ślad w pamięci podobnie pozostaje mały. Jeśli chodzi o dołączanie dużej liczby plików nagłówkowych podczas kompilacji, po raz kolejny nie martw się. Xcode udostępnia funkcję prekompilowanego nagłówka, która przyspiesza czas kompilacji. Kompilując jednocześnie wszystkie nagłówki struktury, nie ma potrzeby ponownej kompilacji nagłówków, chyba że dodasz nową strukturę. W międzyczasie możesz używać dowolnego interfejsu z dołączonych frameworków z niewielkim lub żadnym spadkiem wydajności.

również w moich macros.h umieściłem wiele stałych, takich jak:

// 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
źródło
2
Kolejny przydatny tutaj: #define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ )... do uruchamiania bloków na głównym wątku:async(^{ self.someLabel.text = @":D"; });
Alejandro Iván