iOS Prefix.pch 最佳实践

Posted

技术标签:

【中文标题】iOS Prefix.pch 最佳实践【英文标题】:iOS Prefix.pch best practices 【发布时间】:2010-05-16 19:49:00 【问题描述】:

我见过许多开发人员在他们的 ios 项目的 Prefix.pch 中添加了各种便利宏。

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

【问题讨论】:

关于该主题的详细博文:cimgf.com/2010/05/02/my-current-prefix-pch-file 只需将你的宏放入一个头文件,例如Macros.h,然后将该文件导入你的prefix.pch 我也面临同样的问题...如何在 Xcode 6.1 中解决 【参考方案1】:

Ewww...不要将宏放在 .pch 文件中!根据定义,.pch 文件是项目特定的预编译头文件。它真的不应该在项目的上下文之外使用,它真的不应该包含除#includes 和#imports 之外的任何内容。

如果你有一些宏并且你想在标题之间共享,然后将它们粘贴到它们自己的头文件中 - Common.h 或其他 - 和 #include that 在.pch 的开头。

【讨论】:

你会在 Common.h 中包含什么? 什么都没有;我只会将各种#defines 等...放入其中。【参考方案2】:

对于现代 iOS 和 OS X,人们应该使用模块。新项目默认启用此功能,并使用 @import 完成导入/包含。

模块允许编译器创建模块内容的中间表示(例如框架的头文件)。与 PCH 非常相似,这种中间表示可以在多个翻译之间共享。但是模块更进一步,因为模块不一定是特定于目标的,并且它们的声明不需要本地化(到*.pch)。这种表示可以为您节省大量冗余的编译器工作。

使用模块,您不需要 PCH,您可能应该完全取消它们 - 支持使用 @import 本地依赖项。在这种情况下,PCH 只会使您免于 键入 本地依赖项的包含(IMO 无论如何您都应该这样做)。

现在,如果我们回顾一下最初的问题:您应该避免用各种随机的东西填充您的 PCH;宏、常量、#defines 和各种小库。通常,您应该省略大多数源文件真正不需要的内容。把各种各样的东西放在你的 PCH 中只会增加一堆重量和依赖性。我看到人们将他们链接的所有内容以及更多内容放在 PCH 中。实际上,在大多数情况下,辅助框架通常只需要对少数翻译可见。例如。 “这是我们的 StoreKit 内容 - 让我们仅在 必须 可见的地方导入 StoreKit。具体来说,这 3 种翻译”。这可以缩短构建时间,并帮助您跟踪依赖项,以便您可以更轻松地重用代码。所以在一个 ObjC 项目中,你通常会停留在 Foundation。如果有很多 UI,那么您可以考虑将 UIKit 或 AppKit 添加到您的 PCH。这一切都假设您想要优化构建时间。包含(几乎)所有内容的大型 PCH 的问题之一是删除不必要的依赖项非常耗时。一旦你的项目的依赖增加并且你的构建时间增加,你需要通过消除不必要的依赖来反击,以减少你的构建时间。此外,通常应将任何经常更改的内容排除在您的 PCH 之外。更改需要完全重建。有一些共享 PCH 的选项。如果您使用 PCH,请务必支持共享。

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


参考文献

http://clang.llvm.org/docs/Modules.html http://clang.llvm.org/docs/PCHInternals.html

注意事项

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

【讨论】:

对启用模块的项目进行完全重建意味着什么。你知道这个新的 -Swift.h 桥接头绝对不是 .pch 的合适人选。但我见过人们这样做。因此,您可以查看是否有人这样做。我们在 .pch 中有一个不断变化的标题。因此,每次重新生成 -Swift.h 时,它都会重建 .pch 文件中的所有内容。你同意吗?你有更多的输入? @MadNik 假设您的应用程序的 PCH 包含您正在积极开发的库/框架的主标头。例如:#import "MyLib/MyLib.h"。每当MyLib.h 包含的文件发生更改时,应用程序中的每个源文件都必须重新编译。如果您只在一个源文件中使用 MyLib,那么当 MyLib 发生更改时,只需重新编译该文件。信不信由你,这些天我没有使用任何 PCH 文件。【参考方案3】:

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

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

【讨论】:

我只是好奇。在.PCH 文件中,导入具有各种#import 的Common.h 与直接导入那些#import 有什么区别?这些不一样吗?还是会影响任何性能? 据我所知,没有真正的区别。我猜这更像是一种最佳实践。与其将一堆宏和其他东西塞进你的 PCH 文件,不如只用于#import#include 区别在于可重用性。 PCH 是项目特定的。 Common.h 对许多项目都是通用的。这类似于问为什么您不只是将所有 util 类放入您的项目中,而是创建一个您可以重用的子项目。虽然是一个人为的例子,因为它只是一个简单的复制粘贴......但复制粘贴很顽皮。【参考方案4】:

创建一个头文件“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];

【讨论】:

这里另一个有用的:#define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ ) ... 在主线程上运行块:async(^ self.someLabel.text = @":D"; );

以上是关于iOS Prefix.pch 最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

[转] iOS开发者的Weex伪最佳实践指北

ios 轮换最佳实践

iOS 开发中 Info.plist 变量的最佳实践是啥?

最佳实践 - iOS 中的 NSManagedObjectContextObjectsDidChangeNotification

iOS Facebook SDK - 最佳实践?

iOS 登录 viewcontroller 最佳实践