我正在尝试以对库的尚未使用最新版本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);

07-24 09:46
查看更多