iOS Prefix.pch最佳做法


90

我已经看到许多开发人员在其iOS项目的Prefix.pch中添加了各种便利宏。

您建议(或不建议)向iOS Prefix.pch文件添加什么?您的Prefix.pch是什么样的?


有关此主题的详细博客文章:cimgf.com/2010/05/02/my-current-prefix-pch-file
hpique 2010年

2
只需将宏放入例如头文件中,Macros.h然后将该文件导入到中prefix.pch
Malloc

我也面临着同样的问题...如何在Xcode 6.1中解决
Yalamandarao 2015年

Answers:


121

Ewww…请勿将宏放在.pch文件中!根据定义,.pch文件是项目特定的预编译头。实际上,不应在项目范围之外使用它,#include并且除了s和#imports外,它不应包含任何内容。

如果你有一些宏等,你想报头之间的共享,然后在自己的头文件把脚伸- Common.h或者别的什么-并且#include 在.PCH的开始。


您将在那个Common.h中包含什么?
hpique 2010年

4
没有; 我只将各种#define等放入其中。
bbum 2010年

37

对于现代iOS和OS X,人们应该使用Modules。默认情况下,新项目将启用此功能,并使用导入/包含@import

模块允许编译器创建模块内容的中间表示(例如框架的标头)。就像PCH一样,这种中间表示可以在多个翻译中共享。但是模块将这一步骤更进一步,因为模块不一定是特定于目标的,并且它们的声明不需要本地化(到*.pch)。这种表示形式可以节省大量的编译器工作。

使用模块,您不需要PCH,您可能应该完全取消使用它们-而是对@import依赖项使用local。在这种情况下,PCH只会使您不必键入依赖项本地的包含项(无论如何,您应该执行IMO)。

现在,如果我们回头看最初的问题:您应该避免在PCH中填充各种随机的东西;宏,常量,#defines和各种小库。通常,您应该忽略大多数源文件真正不需要的内容。将各种东西放入您的PCH中只是增加了很多权重和依赖性。我看到人们将他们链接的所有内容以及更多内容都放在了PCH中。实际上,在大多数情况下,辅助框架通常只需要少数翻译可见。例如,“这是我们的StoreKit东西-让我们仅在必须的地方导入StoreKit可见。具体来说,这是3种翻译。“这可以减少构建时间,并帮助您跟踪依赖关系,以便您可以更轻松地重用代码。因此,在ObjC项目中,通常会停在Foundation。如果有很多事情,在UI的基础上,那么您可能考虑将UIKit或AppKit添加到您的PCH中,这都是在假设您要优化构建时间的情况下。您的项目的依存关系会增加,构建时间会增加,您需要通过消除不必要的依存关系来进行反击以减少构建时间,而且,通常应将任何经常更改的内容都排除在PCH中,而更改则需要完全重建。有一些共享PCH的选项。如果您使用PCH,

据我在PCH中输入的内容:几年前,我停止将它们用于绝大多数目标。通常,没有足够的共同点来限定资格。请记住,我写的是C ++,ObjC,ObjC ++和C-编译器为目标中的每个lang发出一个。因此,启用它们通常会导致较慢的编译时间和更高的I / O。最终,增加依赖不是解决复杂项目中依赖问题的好方法。使用多种语言/方言,给定目标所需的依赖项会有很大的差异。不,我不建议将其作为每个项目的最佳选择,但这确实为大型项目中的依赖项管理提供了一些视角。


参考文献


笔记

  • 这个问题最初是在Modules引入前几年提出的。
  • 当前(Xcode 5.0),模块适用于C和ObjC,但不适用于C ++。

对启用了模块的项目进行完全重建是什么意思。您知道这个新的-Swift.h桥接标头绝对不是.pch的正确选择。但是我见过人们这样做。如您所见,是否有人这样做。.pch中的标题总是在变化。因此,每次重新生成-Swift.h时,它都会重建.pch文件中的所有内容。你同意吗?您还有更多输入吗?
MadNik

@MadNik假设您的应用程序的PCH包含您正在积极开发的库/框架的主标头。例如:#import "MyLib/MyLib.h"。任何时候包含的文件MyLib.h每当更改文件时,必须重新编译应用程序中的每个源文件。如果仅在一个源文件中使用MyLib,则在MyLib更改时,仅必须重新编译该文件。信不信由你,这些天我没有使用任何PCH文件。
贾斯汀

8

我同意bbum。我对PCH文件的看法是,它应该只包含#include#import语句。因此,如果您有一堆有用的高级宏,请按照bbum的建议在诸如Common.h#import该文件中定义它们。

我通常会更进一步,将PCH文件用于#import一个名为XXCategories.h(其中XX是使用class命名前缀约定),其中包含#import献给所有我的UIKit和基金类属:NSString+XXAdditions.hUIColor+XXAdditons.h,等。


我只是好奇。在.PCH文件中,导入具有各种内容的Common.h #import#import直接导入它们之间有什么区别?它们不一样吗?还是会影响性能?
Hlung 2012年

据我所知,没有真正的区别。我猜这是一种最佳实践。与其将大量的宏和其他内容推到您的PCH文件中,不如将其用于#import#include
CIFilter 2012年

1
区别在于可重用性。PCH是特定于项目的。Common.h在许多项目中都是通用的。这类似于问为什么不将所有util类放入项目中,而不是创建可以重用的子项目。尽管是一个人为的示例,因为它只是一个简单的复制粘贴...但是复制粘贴很顽皮。
bandejapaisa

6

创建头文件“ macros.h”

将此标头导入Prefix.pch

在此macros.h中放入所有框架和其他重要内容。

如果您担心性能,请不要担心,请看一下苹果所说的话:

标头和性能

如果您担心包含主标头文件可能会导致程序膨胀,请不要担心。因为OS X接口是使用框架实现的,所以这些接口的代码位于动态共享库中,而不位于可执行文件中。此外,在运行时仅将程序使用的代码加载到内存中,因此内存占用量同样很小。至于在编译过程中包含大量头文件,请不要担心。Xcode提供了预编译的头文件功能,以加快编译时间。通过一次编译所有框架标头,除非您添加新框架,否则无需重新编译标头。同时,您可以使用包含的框架中的任何接口,而几乎不会降低性能。

同样在我的macros.h中,我输入了很多常量,例如:

// 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];

2
另一个有用的方法是:#define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ )...在主线程上运行块:async(^{ self.someLabel.text = @":D"; });
AlejandroIván16年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.