关于出现Precompiled header uses __DATE__ or __TIME__警告的探讨和解决方案。

在使用旧版本YYKit的时候,出现了这个警告。具体警告的代码在YYKitMarco.h里面

static inline  NSDate *YYCompileTime() {
    NSString *timeStr = [NSString stringWithFormat:@"%s %s",__DATE__, __TIME__];
    NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"MMM dd yyyy HH:mm:ss"];
    [formatter setLocale:locale];
    return [formatter dateFromString:timeStr];
}

作者给的解决方式在里面 https://github.com/ibireme/YYKit/issues/152,使用新版YYKit即可解决问题,不过作者自己没有分析出现的原因和问题所在。本文将分析问题的根源。

需要明确一点,在不使用Pch文件的时候是不会出现这个错误的,所以需要结合PCH文件的原理来分析问题的所在,我们从以下两方面分析问题:

  • PCH文件

  • __DATE____TIME__等标准预编译宏的用途


PCH文件

首先一点,我们自己建立是 prefix header,就是预编译头文件。在Xcode 6之后,新建项目的时候不会自动生成PCH文件。
预编译的初衷是为了提高整体的编译速度,当项目中很多的文件都需要引用一些共同个头文件时。我们可以把该文件放入PCH文件,当编译项目中的源文件时,我们首先加载PCH文件(作为前缀头),作为该源文件头的替代。

加载PCH文件比重新解析存储在PCH文件中的头文件显着更快。因此,预编译头文件设计是为了最小化读取PCH文件的成本。】

PCH文件编译的时间肯定是早于源文件的编译时间。这就使得源文件如果使用了和编译相关的数据就会出现错误。

参考资料:http://osr600doc.sco.com/en/SDK_cprog/CCCS_CPrecompiledHeaders.html


__DATE____TIME__等标准预编译宏的用途

__DATE__
This macro expands to a string constant that describes the date on which the preprocessor is being run. The string constant contains eleven characters and looks like “Feb 12 1996”. If the day of the month is less than 10, it is padded with a space on the left.
If GCC cannot determine the current date, it will emit a warning message (once per compilation) and__DATE__will expand to “??? ?? ????”.

__TIME__
This macro expands to a string constant that describes the time at which the preprocessor is being run. The string constant contains eight characters and looks like “23:59:01”.
If GCC cannot determine the current time, it will emit a warning message (once per compilation) and __TIME__ will expand to “??:??:??”.

__DATE____TIME__都是代表了编译当前文件所用的时间,如果使用内联函数,我们在编译PCH文件的时候就会取得该值,这个值在以后的编译文件中都会保持不变。所以获取到的编译时间都是编译PCH文件的时间而不是编译当前文件的时间。

参考地址:https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

测试两种不同的写法的时间差别。新建了一个类,在类里面再获取一次编译时间。然后对比使用两种不同情况的时间区别,为了方便起见,我修改了YYKitMarco里面发返回值为NSString。

#import "TestObject.h"
#import <sys/time.h>

@implementation TestObject

- (void)printPchCompileTime {
    NSString *date =   YYCompileTime();
    NSLog(@"YYTime time is %@",date);
}

- (void)printNormalCompileTime {
    NSString *date =   NormalCompileTime();

}

static inline NSDate *NormalCompileTime() {
    NSString *timeStr = [NSString stringWithFormat:@"%s %s",__DATE__, __TIME__];
//    NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
//    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
//    [formatter setDateFormat:@"MMM dd yyyy HH:mm:ss"];
//    [formatter setLocale:locale];
//    return [formatter dateFromString:timeStr];
    NSLog(@"slef time is %@",timeStr);
    return nil;
}


首先是使用内联函数的形式。

static inline  NSDate *YYCompileTime() {
    NSString *timeStr = [NSString stringWithFormat:@"%s %s",__DATE__, __TIME__];
    NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"MMM dd yyyy HH:mm:ss"];
    [formatter setLocale:locale];
    return [formatter dateFromString:timeStr];
}


时间差别如下,YYKitTime是通过PCH文件里面的内联函数获取到的时间,self time是通过自己源文件获取到的时间。

2016-11-28 10:47:57.611526 PCHDemo[2571:1773812] YYTime time is Nov 28 2016 10:47:52
2016-11-28 10:47:57.611637 PCHDemo[2571:1773812] slef time is Nov 28 2016 10:47:52

乍一看时间是一样的,因为我们的测试工程很小,时间差距不大。我们修改一下TestObject里面的代码,重新编译

2016-11-28 10:48:14.738561 PCHDemo[2573:1774045] YYTime time is Nov 28 2016 10:47:52
2016-11-28 10:48:14.738678 PCHDemo[2573:1774045] slef time is Nov 28 2016 10:48:09

可以看到YYTime时间一直保持和PCH文件第一次编译时间保持一致。

然后使用宏的形式。

static inline NSString *_YYCompileTime(const char *data, const char *time) {
    NSString *timeStr = [NSString stringWithFormat:@"%s %s",data,time];
//    NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
//    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
//    [formatter setDateFormat:@"MMM dd yyyy HH:mm:ss"];
//    [formatter setLocale:locale];
    return timeStr;
}

/**
 Get compile timestamp.
 @return A new date object set to the compile date and time.
 */
#ifndef YYCompileTime
// use macro to avoid compile warning when use pch file
#define YYCompileTime() _YYCompileTime(__DATE__, __TIME__)
#endif

时间差别如下,YYKitTime是通过PCH文件里面的内联函数获取到的时间,self time是通过自己源文件获取到的时间。

2016-11-28 10:49:12.458607 PCHDemo[2575:1774369] YYTime time is Nov 28 2016 10:49:07
2016-11-28 10:49:12.458713 PCHDemo[2575:1774369] slef time is Nov 28 2016 10:49:07

在第一次编译后,修改一下TestObject里面的代码重新编译。

2016-11-28 10:49:35.608224 PCHDemo[2577:1774614] YYTime time is Nov 28 2016 10:49:31
2016-11-28 10:49:35.608347 PCHDemo[2577:1774614] slef time is Nov 28 2016 10:49:31

可以看到使用宏之后,两者获取到的时间保持一致。达到了预期的要求。
宏是在具体源文件之中展开的。所以编译时间获取的是当前编译文件的时间。


其他参考文档:
http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20150831/137365.html
DATE.html”>http://www.cprogramming.com/reference/preprocessor/DATE.html
http://clang.llvm.org/docs/DiagnosticsReference.html
http://blog.chinaunix.net/uid-30127490-id-5063666.html
https://msdn.microsoft.com/zh-cn/library/hd8sctab.aspx
http://stackoverflow.com/questions/23648593/what-is-prefix-pch-file-in-xcode
http://clang.llvm.org/docs/PCHInternals.html
http://clang.llvm.org/docs/Modules.html
http://useyourloaf.com/blog/modules-and-precompiled-headers/
http://gamesfromwithin.com/the-care-and-feeding-of-pre-compiled-headers
http://stackoverflow.com/questions/27100847/llvm-6-prefix-header-vs-preprocessor-macros-not-used-in-precompiled-headers
http://osr600doc.sco.com/en/SDK_cprog/CCCS_CPrecompiledHeaders.html
https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值