iOS Prefix.pch best practices
I have seen many developers that add various convenience macros to the Prefix.pch of their iOS projects.
What do (or don't) you recommend adding开发者_如何转开发 to the iOS Prefix.pch file? What does your Prefix.pch look like?
Ewww… don't put macros in a .pch file! A .pch file is, by definition, a project specific precompiled header. It really shouldn't be used beyond the context of the project and it really shouldn't contain anything but #include
s and #import
s.
If you have some macros and such that you want to share between headers, then stick 'em in a header file of their own — Common.h
or whatever — and #include
that at the beginning of the .pch.
For modern iOS and OS X, people should be using Modules. This is enabled by default for new projects, and importing/inclusion is accomplished using @import
.
Modules allow the compiler to create an intermediate representation of the contents of a module (e.g. a framework's headers). Much like a PCH, this intermediate representation may be shared across multiple translations. But modules take this one step further because a module is not necessarily target specific, and their declarations need not be localized (to a *.pch
). This representation can save you a ton redundant compiler work.
Using modules, you do not need a PCH, and you probably should just do away with them entirely -- in favor of using @import
local to the dependency. In that case, a PCH is only saving you from typing inclusions local to dependencies (which IMO you should be doing anyway).
Now, if we look back to the original question: You should avoid filling your PCH with all sorts of random things; Macros, constants, #defines
, and all sorts of little libraries. Generally, you should omit what really is unnecessary to the majority of your source files. Putting all sorts of stuff in your PCH is just adding a bunch of weight and dependency. I see people put everything they link and more to in the PCH. In reality, auxiliary frameworks typically only need to be visible to a few translations in most cases. E.g. "Here is our StoreKit stuff - let's import StoreKit only where it must be visible. Specifically, these 3 translations". This keeps your build times down, and helps you keep track of your dependencies, so that you may reuse code more easily. So in an ObjC project, you would usually stop at Foundation. If there is a lot of UI, then you might consider adding UIKit or AppKit to your PCH. This is all assuming you want to optimize build times. One of the problems with large PCHs that include (nearly) everything is that removing unnecessary dependencies is very time consuming. Once your project's dependencies grow and your build times go up, you need to fight back by eliminating unnecessary dependencies in order to reduce your build times. Also, anything that changes often should generally be kept out of your PCH. A change requires a full rebuild. There are some options to share PCHs. If you use PCHs, do aim to support sharing.
As far as what I put in my PCH: I stopped using them for the vast majority of targets years ago. There just usually is not enough in common to qualify. Bear in mind, I write C++, ObjC, ObjC++ and C - the compiler emits one for each lang in your target. So enabling them often resulted in slower compile times and higher I/O. Ultimately, increasing dependency is not a good way to fight dependency in complex projects. Working with multiple languages/dialects, there are is much variation in the dependencies required for a given target. No, I would not advise that as optimal for every project, but that does give some perspective to dependency management in larger projects.
References
- http://clang.llvm.org/docs/Modules.html
- http://clang.llvm.org/docs/PCHInternals.html
Notes
- This question was originally asked a few years before Modules' introduction.
- Presently (Xcode 5.0), modules work for C and ObjC, but not C++.
I agree with bbum. My take on the PCH file is that it should contain pretty much only #include
or #import
statements. So if you have a bunch of helpful, high-level macros, define them in something like Common.h
and #import
that file, as bbum suggested.
I usually go a step further and use the PCH file to #import
a file called XXCategories.h
(where XX
is the class naming prefix convention you use) that contains #import
s for all my UIKit and Foundation class categories: NSString+XXAdditions.h
, UIColor+XXAdditons.h
, etc.
create an header file "macros.h"
import this header into Prefix.pch
In this macros.h put all the frameworks and other important things.
If you are worried about performance, don't worry, look what apple says:
Headers and Performance
If you are worried that including a master header file may cause your program to bloat, don’t worry. Because OS X interfaces are implemented using frameworks, the code for those interfaces resides in a dynamic shared library and not in your executable. In addition, only the code used by your program is ever loaded into memory at runtime, so your in-memory footprint similarly stays small. As for including a large number of header files during compilation, once again, don’t worry. Xcode provides a precompiled header facility to speed up compile times. By compiling all the framework headers at once, there is no need to recompile the headers unless you add a new framework. In the meantime, you can use any interface from the included frameworks with little or no performance penalty.
also in my macros.h I put a lot of constants like:
// 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];
精彩评论