如何打印方法名称和行号并有条件地禁用NSLog?

451

我正在做一个有关Xcode调试的演示,想获取更多关于高效使用NSLog的信息。

具体来说,我有两个问题:

  • 有没有一种简单的方法可以NSLog当前方法的名称/行号?
  • 有没有一种简单的方法在编译发布代码之前“禁用”所有NSLog?

13
第一个问题是,当收藏(星标)数超过点赞数时...... +1。 - Fahim Parkar
13个回答

598

这里有一些我经常使用的关于NSLog的实用宏:

#ifdef DEBUG
#   define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#   define DLog(...)
#endif

// ALog always displays output regardless of the DEBUG setting
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)

DLog宏用于仅在设置了DEBUG变量时输出(-DDEBUG为调试配置的项目C标志)。

ALog将始终输出文本(例如常规NSLog)。

输出结果(例如ALog(@"Hello world"))将如下所示:

-[LibraryController awakeFromNib] [Line 364] Hello world

1
这可以防止参数的宏扩展。 - diederikh
我来晚了,但我不喜欢在宏定义中使用带有分号 ; 字符,以至于我只是因为这个原因下投了你的评论。让 DLog 看起来像 C/C++/Obj-C 中的每个其他语句,并要求在使用它时添加一个分号,这既更简单,又更有沟通性。这样做使得 DLog() 和 NSLog() 更加可互换(如今,如果你将所有的 DLog 词法地替换为 NSLog,你将不得不添加回所有那些你避免输入的 ;,这是不正确的)。 - Cubs Fan Ron
如果不在DEBUG模式下运行,这段代码会在Xcode 5.1中导致未使用变量的错误 :( - yonix
@yonix 这是有意的。如果变量仅在DEBUG中需要,请使用#ifdef DEBUG来声明它。或者忽略警告。 - Cœur
我不知道为什么,在我的情况下,我一直收到这个构建错误:Called object of type NSString is not a function or function pointer。所以我不得不删除括号中的 fmt 表达式:#define ALog(fmt, ...) NSLog(@"%s [Line %d] " fmt, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) - Gomino
显示剩余10条评论

142
我从上面拿到了DLogALog,并添加了ULog,它会弹出一个UIAlertView消息。
总结如下:
  • DLog仅在设置了DEBUG变量时才会像NSLog一样输出
  • ALog始终会像NSLog一样输出
  • ULog仅在设置了DEBUG变量时才会显示UIAlertView
#ifdef DEBUG
#   define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#   define DLog(...)
#endif
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#ifdef DEBUG
#   define ULog(fmt, ...)  { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"%s\n [Line %d] ", __PRETTY_FUNCTION__, __LINE__] message:[NSString stringWithFormat:fmt, ##__VA_ARGS__]  delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; }
#else
#   define ULog(...)
#endif

这是它的外观:

Debug UIAlertView

+1 Diederik


我将扩展我的ALog+DLog代码,并加入ULog。非常有用。 - neoneye
如果不在DEBUG模式下运行,这段代码会在Xcode 5.1中导致未使用变量错误 :( - yonix
为什么有些 #define 指令以分号结尾? - Monstieur
@Locutus,在DLog语句后面不需要加分号。这很有用,因为如果你这样做,在发布版本中,DLog会被编译为空,你的代码中会留下一个悬空的分号。这不是错误,但如果它跟在另一个分号后面,根据你的设置,可能会引发警告。 - Zev Eisenberg

74
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

输出文件名、行号和函数名:

/proj/cocoa/cdcli/cdcli.m 121 managedObjectContext managedObjectContext

__FUNCTION__ 在C++中显示的是混淆的函数名,__PRETTY_FUNCTION__ 显示的是漂亮的函数名,在cocoa中它们看起来相同。

我不确定禁用NSLog的正确方法是什么,我做了以下尝试:

#define NSLog

没有任何日志输出出现,但我不知道这是否有任何副作用。


20

这里有一个我们使用的大型调试常量集合。享用。

// Uncomment the defitions to show additional info.

//  #define DEBUG

//  #define DEBUGWHERE_SHOWFULLINFO

//  #define DEBUG_SHOWLINES
//  #define DEBUG_SHOWFULLPATH
//  #define DEBUG_SHOWSEPARATORS
//  #define DEBUG_SHOWFULLINFO


// Definition of DEBUG functions. Only work if DEBUG is defined.
#ifdef DEBUG 

    #define debug_separator() NSLog( @"────────────────────────────────────────────────────────────────────────────" );

    #ifdef DEBUG_SHOWSEPARATORS
        #define debug_showSeparators() debug_separator();
    #else
        #define debug_showSeparators()
    #endif

    /// /// /// ////// ///// 

    #ifdef DEBUG_SHOWFULLPATH
        #define debug_whereFull() debug_showSeparators(); NSLog(@"Line:%d : %s : %s", __LINE__,__FILE__,__FUNCTION__); debug_showSeparators(); 
    #else
        #define debug_whereFull() debug_showSeparators(); NSLog(@"Line:%d : %s : %s", __LINE__,[ [ [ [NSString alloc] initWithBytes:__FILE__ length:strlen(__FILE__) encoding:NSUTF8StringEncoding] lastPathComponent] UTF8String ] ,__FUNCTION__); debug_showSeparators(); 
    #endif

    /// /// /// ////// ///// 

    #define debugExt(args,...) debug_separator(); debug_whereFull(); NSLog( args, ##__VA_ARGS__); debug_separator();

    /// /// /// ////// ///// Debug Print Macros

    #ifdef DEBUG_SHOWFULLINFO
        #define debug(args,...) debugExt(args, ##__VA_ARGS__);
    #else
        #ifdef DEBUG_SHOWLINES
            #define debug(args,...) debug_showSeparators(); NSLog([ NSString stringWithFormat:@"Line:%d : %@", __LINE__, args ], ##__VA_ARGS__); debug_showSeparators();
        #else
            #define debug(args,...) debug_showSeparators(); NSLog(args, ##__VA_ARGS__); debug_showSeparators();
        #endif
    #endif

    /// /// /// ////// ///// Debug Specific Types

    #define debug_object( arg ) debug( @"Object: %@", arg );
    #define debug_int( arg ) debug( @"integer: %i", arg );
    #define debug_float( arg ) debug( @"float: %f", arg );
    #define debug_rect( arg ) debug( @"CGRect ( %f, %f, %f, %f)", arg.origin.x, arg.origin.y, arg.size.width, arg.size.height );
    #define debug_point( arg ) debug( @"CGPoint ( %f, %f )", arg.x, arg.y );
    #define debug_bool( arg )   debug( @"Boolean: %@", ( arg == YES ? @"YES" : @"NO" ) );

    /// /// /// ////// ///// Debug Where Macros

    #ifdef DEBUGWHERE_SHOWFULLINFO
        #define debug_where() debug_whereFull(); 
    #else
        #define debug_where() debug(@"%s",__FUNCTION__); 
    #endif

    #define debug_where_separators() debug_separator(); debug_where(); debug_separator();

    /// /// /// ////// /////

#else
    #define debug(args,...) 
    #define debug_separator()  
    #define debug_where()   
    #define debug_where_separators()  
    #define debug_whereFull()   
    #define debugExt(args,...)
    #define debug_object( arg ) 
    #define debug_int( arg ) 
    #define debug_rect( arg )   
    #define debug_bool( arg )   
    #define debug_point( arg )
    #define debug_float( arg )
#endif

你会在哪里以什么方式实现这个? - uplearned.com

20

有一个新技巧,没有任何答案提到。你可以使用printf代替NSLog。这样会给你一个清晰的日志:

使用NSLog会得到像这样的东西:

2011-11-03 13:43:55.632 myApp[3739:207] Hello Word

但是使用 printf 你只能得到:

Hello World

使用此代码

#ifdef DEBUG
    #define NSLog(FORMAT, ...) fprintf(stderr,"%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
    #define NSLog(...) {}              
#endif

16

我的回答可能会对这个问题有所帮助,它看起来与Diederik提出的问题类似。您还可以将对NSLog()的调用替换为自己的自定义日志记录类的静态实例,这样您就可以添加调试/警告/错误消息的优先级标志,并将消息发送到文件或数据库以及控制台,或者几乎任何您能想到的操作。

#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self, 
              [[NSString stringWithUTF8String:__FILE__] lastPathComponent], 
              __LINE__, 
              [NSString stringWithFormat:(s), 
              ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... ) 
#endif

因为你避开了苹果正在试图废弃的“%s”格式说明符,并且避免了2015年新引入的“-Wcstring-format-directive” Clang警告。 - Jeff

13

如果有人对宏过敏,想要禁用所有的NSLog输出,以下代码可供编译使用:

void SJLog(NSString *format,...)
{
    if(LOG)
    {   
        va_list args;
        va_start(args,format);
        NSLogv(format, args);
        va_end(args);
    }
}

并且,使用它几乎像使用NSLog一样:

SJLog(@"bye bye NSLogs !");

从这篇博客中获取:https://whackylabs.com/logging/ios/2011/01/19/ios-moving-in-and-out-of-nslogs/


12

将现有的NSLog转换为显示调用它们的行号和类很容易。只需在前缀文件中添加一行代码即可:

#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)

3
太棒了!你如何在Swift中实现这个? - uplearned.com
@AddisDev 我最喜欢这个。非常干净简洁。我只使用NSLog。我根本不知道DLog和ULog是什么!谢谢。已点赞... - Charles Robertson
@AddisDev,我真的不明白为什么苹果不默认将这个至关重要的数据添加到NSLog()中?太奇怪了... - Charles Robertson

11
为了补充上面的答案,有时在调试中使用NSLog的替代方案非常有用。例如,消除每行上的日期和进程名称/ID信息可以使输出更易读且更快。

以下链接提供了许多有用的资料,可以使简单的日志记录变得更加美观。

http://cocoaheads.byu.edu/wiki/a-different-nslog


9
这很简单,举个例子。

-(void)applicationWillEnterForeground:(UIApplication *)application {

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

输出结果: -[AppDelegate applicationWillEnterForeground:]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接