我正在尝试以对库的尚未使用最新版本OS(iOS或macOS)的用户保持向后兼容性的方式,将对新的日志记录和 Activity 跟踪API的支持添加到库中。我正在为每个日志记录级别定义自定义日志记录宏,然后为较旧的OS定义为NSLog
。我已经开始工作了,有一个问题。
如果希望新API出现在日志输出中,则新API要求您将所有非恒定,非标量值标记为public
。这是我的宏调用的样子:
UZKLogInfo("Reading file %{public}@ from archive", fileName);
使用包含
os_log
(例如iOS 10.0或更高版本)的SDK可以很好地进行编译,但是当我使用早期版本进行编译时,我的宏又回到了NSLog
,我得到了编译器警告:在os_log()/ os_trace()之外使用'public'格式说明符注释
日志行显示如下:
Reading file <decode: missing data> from archive
这是我的宏定义的简化版本(仅包括
info
定义并简化了条件:#if UNIFIED_LOGGING_SUPPORTED
@import os.log;
#define UZKLogInfo(format, ...) os_log_info(OS_LOG_DEFAULT, format, ##__VA_ARGS__);
#else // Fall back to regular NSLog
#define UZKLogInfo(format, ...) NSLog(@format, ##__VA_ARGS__);
#endif
在备用情况下,是否有任何方法可以从
format
中剥离“{public}”文本(某种形式的字符串替换?)?还是有另一种方式可以支持旧的和新的API,而又不会放弃我一直在日志中显示的信息级别?我需要使用一个宏(根据last year's WWDC session on the topic,否则我将丢失 call 站点元数据。 最佳答案
我选择在宏中执行NSString
替换,并禁止编译器警告作为其一部分,因此可以针对每一行执行此操作,而不是针对整个文件或项目进行全局处理。看起来像这样:
#if UNIFIED_LOGGING_SUPPORTED
@import os.log;
#define UZKLogInfo(format, ...) os_log_info(OS_LOG_DEFAULT, format, ##__VA_ARGS__);
#else // Fall back to regular NSLog
#define _removeLogFormatTokens(format) [@format stringByReplacingOccurrencesOfString:@"{public}" withString:@""]
#define _stringify(a) #a
#define _nsLogWithoutWarnings(format, ...) \
_Pragma( _stringify( clang diagnostic push ) ) \
_Pragma( _stringify( clang diagnostic ignored "-Wformat-nonliteral" ) ) \
_Pragma( _stringify( clang diagnostic ignored "-Wformat-security" ) ) \
NSLog(_removeLogFormatTokens(format), ##__VA_ARGS__); \
_Pragma( _stringify( clang diagnostic pop ) )
#define UZKLogInfo(format, ...) _nsLogWithoutWarnings(format, ##__VA_ARGS__);
#endif
就像这样:
UZKLogInfo("Message: %@", anObjectToLog);