【问题标题】:Avoiding messiness with debug stuff in code避免代码中的调试内容混乱
【发布时间】:2011-07-20 12:05:00
【问题描述】:

当我写东西时,一半的努力往往是添加清晰简洁的调试输出,或者在需要调试时可以启用/禁用的功能

调试功能 的一个示例是一个下载器类,我可以在其中打开#define 使其“假装”下载文件并将我已经拥有的文件交还给我。这样我就可以测试用户下载文件时会发生什么,而不必每次都等待网络物理抓取文件。这是一个很棒的功能,但是使用#ifdefs 会使代码变得更加混乱。

我最终得到了一堆#defines 之类的

// #define DEBUG_FOOMODULE_FOO
// #define DEBUG_BARMODULE_THINGAMAJIG
// ...

对于我想看的东西没有注释。代码本身类似于

- (void)something
{
    #ifdef DEBUG_FOOMODULE_FOO
    DebugLog(@"something [x = %@]", x);
    #endif
    // ...
    #ifdef DEBUG_FOOMODULE_MOO
    // etc
}

这对于编写/维护代码非常有用,但对代码的外观没有任何作用。

无论如何,人们如何编写轻松的即时长期调试“东西”?

注意:我在这里不仅在谈论 NSLogging...我还在谈论上面的假装下载之类的东西。

【问题讨论】:

  • 好奇:在发布版本中留下NSLog 语句有问题吗?它是否会损害性能、填充设备日志或以其他方式提供不良结果?
  • Apple 确实提到您应该在发布代码时从代码中删除 NSLog 条目,所以我认为确实如此,是的。 (我实际上找不到这个,但我几乎可以肯定,当我开始编码时,我已经在 developer.apple.com 上的某个地方读过它。)
  • 我相信我也记得在某处读过它。可能不是一个坏主意。也许下一个版本的应用程序我会添加宏和搜索/替换。
  • 我已经看到在我的应用程序中使用 NSLog 语句的性能受到影响。把它们留在里面确实很痛。

标签: iphone objective-c debugging coding-style code-cleanup


【解决方案1】:

我在编写自己的库之前阅读了几个库,并看到了两种方法:宏 + C 函数 (NSLogger) 或宏 + 单例 (GTMLogger, Cocoa Lumberjack)。

我使用宏 + 单例编写了我的幼稚实现 here。我在运行时这样做:

[Logger singleton].logThreshold = kDebug;
trace(@"hi %@",@"world); // won't show
debug(@"hi %@",@"world);

您可以对包而不是日志级别执行相同的操作。如果我想要它消失,我会更改#defines。以下是涉及的代码:

#define trace(args...) [[Logger singleton] debugWithLevel:kTrace line:__LINE__ funcName:__PRETTY_FUNCTION__ message:args];

if (level>=logThreshold){
  // ...
} 

如果您想对 Lumberjack 进行更复杂的研究,它有一个注册类工具来切换某些类的日志记录。

【讨论】:

  • 看了一圈。我想最大的问题是我有时还会在这些#defines 中包含用于调试目的的特殊代码。我开始意识到这可能是一个不好的方法,不管你怎么做......:/
【解决方案2】:

拥有两个函数,然后在运行时或编译时适当地选择它们对我来说似乎是一种干净的方法。这使得拥有一个download.c 和一个download_debug.c 文件成为可能,除了不同的实现之外,它们具有相同的功能。如果您使用 -DDEBUG 进行构建,请与相应的链接。

否则,使用函数指针同样适用于运行时选择函数。

如果你坚持让调试代码散布在你的函数中,你几乎把自己弄得一团糟:) 但是,你当然可以将这些 sn-ps 分解为单独的函数,然后执行上述操作(或使它们像 DLog 示例中那样无操作)。

【讨论】:

    【解决方案3】:

    对于您的情况,您可以有单独的日志记录宏,例如 MooLogFooLog,它们都基于单独的标志有条件地编译。

    #ifdef DEBUG_FOO
    #   define FooLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
    #else
    #   define FooLog(...)
    #endif
    
    #ifdef DEBUG_MOO
    #   define MooLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
    #else
    #   define MooLog(...)
    #endif
    

    你现在到处都是复杂的逻辑:

    - (void)something
    {
        // This only gets logged if the "DEBUG_FOO" flag is set.
        FooLog(@"something [x = %@]", x);
        // This only gets logged if the "DEBUG_MOO" flag is set.
        MooLog(@"Something else [y = %@]", y);
    }
    

    【讨论】:

    • 好吧,我在上面的代码中将其简化为 NSLog,但我已经在使用执行此操作的代码。我要问的是在哪里可以启用/禁用“调试模块”。我想我可以为每个模块编写一堆#define 函数来减少#if,但是没有更好的方法来做到这一点吗? :|
    猜你喜欢
    • 1970-01-01
    • 2018-12-07
    • 2014-11-07
    • 1970-01-01
    • 2012-01-23
    • 2021-04-13
    • 2019-11-21
    • 1970-01-01
    • 2011-06-28
    相关资源
    最近更新 更多