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


446

我正在做有关Xcode调试的演示,并希望获得有关有效使用NSLog的更多信息。

特别是,我有两个问题:

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

12
第一个问题是最喜欢的(明星)更多,然后投票…… +1 ..
Fahim Parkar 2014年

Answers:


592

这是我经常使用的有关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变量(调试配置的项目C标志中为-DDEBUG)时才用于输出。

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

输出(例如ALog(@“ Hello world”))将如下所示:

-[LibraryController awakeFromNib] [Line 364] Hello world

为什么你有一个 ##?我以为这是为了将参数粘合在一起,但是您并没有粘合任何东西。
Casebash 2010年

1
这阻止了参数可能的宏扩展
deaderikh

通常,宏可能会发生这种情况。一些宏产生多行。另一个总是使用大括号;-)的参数。
deaderikh 2010年

great和cocos2d api具有类似的log语句。
李允(Yoon Lee)

如何(@"%s [Line %d] " fmt)导致fmt将追加到控制字符串?除了此调试宏之外,我没有看到此语法。
罗伯特·奥特曼

141

我已经采取DLogALog从上面,并添加ULog这引起了一UIAlertView消息。

总结一下:

  • DLogNSLog仅在设置DEBUG变量时输出
  • ALog 总是输出像 NSLog
  • ULogUIAlertView仅在设置DEBUG变量时 显示
#ifdef调试
#定义DLog(fmt,...)NSLog((@“%s [Line%d]” fmt),__PRETTY_FUNCTION__,__LINE__,## __ VA_ARGS__);
#其他
#定义DLog(...)
#万一
#define ALog(fmt,...)NSLog((@“%s [Line%d]” fmt),__PRETTY_FUNCTION__,__LINE__,## __ VA_ARGS__);
#ifdef调试
#define ULog(fmt,...){UIAlertView * alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@“%s \ n [Line%d]”,__PRETTY_FUNCTION__,__LINE__]消息:[NSString stringWithFormat:fmt ,## __ VA_ARGS__]代表:无cancelButtonTitle:@“确定” otherButtonTitles:无];[警报显示];}
#其他
#定义ULog(...)
#万一

看起来是这样的:

调试UIAlertView

+1 Diederik


我还将使用ULog扩展我的ALog + DLog代码。很有用。
neoneye 2011年

如果未在DEBUG中运行,则此代码会导致Xcode 5.1中出现未使用的变量错误:(
yonix 2014年

为什么某些#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__显示了不错的函数名称,在可可中,它们看起来相同。

我不确定禁用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

19

有一个没有答案的新技巧。您可以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

因为您避开了%sApple正在尝试弃用的格式说明符,并避免-Wcstring-format-directive了2015
Jeff



11

更改现有的NSLog以显示行号和调用它们的类很容易。在您的前缀文件中添加一行代码:

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

3
这很棒!您将如何迅速做到这一点?
uplearnedu.com

@AddisDev我最喜欢这个。非常干净和简单。我只使用NSLog。我不知道什么是DLog和ULog!谢谢。投票最多...
Charles Robertson

@AddisDev我真的不明白,为什么苹果默认情况下不将这一至关重要的数据添加到NSLog()中?奇怪...
查尔斯·罗伯逊,

8

很简单,例如

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

    NSLog(@"%s", __PRETTY_FUNCTION__);

}

输出: -[AppDelegate applicationWillEnterForeground:]


5

在以上答案的基础上,我I窃并提出了以下内容。还添加了内存日志记录。

#import <mach/mach.h>

#ifdef DEBUG
#   define DebugLog(fmt, ...) NSLog((@"%s(%d) " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#   define DebugLog(...)
#endif


#define AlwaysLog(fmt, ...) NSLog((@"%s(%d) " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);


#ifdef DEBUG
#   define AlertLog(fmt, ...)  { \
    UIAlertView *alert = [[UIAlertView alloc] \
            initWithTitle : [NSString stringWithFormat:@"%s(Line: %d) ", __PRETTY_FUNCTION__, __LINE__]\
                  message : [NSString stringWithFormat : fmt, ##__VA_ARGS__]\
                 delegate : nil\
        cancelButtonTitle : @"Ok"\
        otherButtonTitles : nil];\
    [alert show];\
}
#else
#   define AlertLog(...)
#endif



#ifdef DEBUG
#   define DPFLog NSLog(@"%s(%d)", __PRETTY_FUNCTION__, __LINE__);//Debug Pretty Function Log
#else
#   define DPFLog
#endif


#ifdef DEBUG
#   define MemoryLog {\
    struct task_basic_info info;\
    mach_msg_type_number_t size = sizeof(info);\
    kern_return_t e = task_info(mach_task_self(),\
                                   TASK_BASIC_INFO,\
                                   (task_info_t)&info,\
                                   &size);\
    if(KERN_SUCCESS == e) {\
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; \
        [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; \
        DebugLog(@"%@ bytes", [formatter stringFromNumber:[NSNumber numberWithInteger:info.resident_size]]);\
    } else {\
        DebugLog(@"Error with task_info(): %s", mach_error_string(e));\
    }\
}
#else
#   define MemoryLog
#endif

4

DLog的新增功能。与其完全从已发布的应用程序中删除调试,请仅将其禁用。当用户遇到问题(需要调试)时,只需告诉如何在已发布的应用程序中启用调试即可并通过电子邮件请求日志数据即可。

简短版本:创建全局变量(是的,懒惰和简单的解决方案)并按如下所示修改DLog:

BOOL myDebugEnabled = FALSE;
#define DLog(fmt, ...) if (myDebugEnabled) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);

Jomnius iLessons iLearned的更长回答:如何在发布的应用程序中进行动态调试日志记录


3

一段时间以来,我一直在使用上面提到的一些宏站点。我的重点是在控制台中登录,重点是受控和过滤的详细程度;如果您不介意很多日志行,但想轻松地打开或关闭它们的批次,那么您可能会发现这很有用。

首先,我可以选择使用上面@Rodrigo所述的printf替换NSLog

#define NSLOG_DROPCHAFF//comment out to get usual date/time ,etc:2011-11-03 13:43:55.632 myApp[3739:207] Hello Word

#ifdef NSLOG_DROPCHAFF
#define NSLog(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#endif

接下来,我打开或关闭登录。

#ifdef DEBUG
#define LOG_CATEGORY_DETAIL// comment out to turn all conditional logging off while keeping other DEBUG features
#endif

在主块中,定义与您的应用程序中的模块相对应的各种类别。还定义一个日志记录级别, 超过该级别将不会调用日志记录调用。然后定义各种风格的NSLog输出

#ifdef LOG_CATEGORY_DETAIL

    //define the categories using bitwise leftshift operators
    #define kLogGCD (1<<0)
    #define kLogCoreCreate (1<<1)
    #define kLogModel (1<<2)
    #define kLogVC (1<<3)
    #define kLogFile (1<<4)
    //etc

    //add the categories that should be logged...
    #define kLOGIFcategory kLogModel+kLogVC+kLogCoreCreate

    //...and the maximum detailLevel to report (use -1 to override the category switch)
    #define kLOGIFdetailLTEQ 4

    // output looks like this:"-[AppDelegate myMethod] log string..."
    #   define myLog(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s " format), __PRETTY_FUNCTION__, ##__VA_ARGS__);}

    // output also shows line number:"-[AppDelegate myMethod][l17]  log string..."
    #   define myLogLine(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s[l%i] " format), __PRETTY_FUNCTION__,__LINE__ ,##__VA_ARGS__);}

    // output very simple:" log string..."
    #   define myLogSimple(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"" format), ##__VA_ARGS__);}

    //as myLog but only shows method name: "myMethod: log string..."
    // (Doesn't work in C-functions)
    #   define myLog_cmd(category,detailLevel,format,...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%@: " format), NSStringFromSelector(_cmd), ##__VA_ARGS__);}

    //as myLogLine but only shows method name: "myMethod>l17: log string..."
    #   define myLog_cmdLine(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%@>l%i: " format), NSStringFromSelector(_cmd),__LINE__ , ##__VA_ARGS__);}

    //or define your own...
   // # define myLogEAGLcontext(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s>l%i (ctx:%@)" format), __PRETTY_FUNCTION__,__LINE__ ,[EAGLContext currentContext], ##__VA_ARGS__);}

#else
    #   define myLog_cmd(...)
    #   define myLog_cmdLine(...)
    #   define myLog(...)
    #   define myLogLine(...)
    #   define myLogSimple(...)
    //#   define myLogEAGLcontext(...)
#endif

因此,在使用kLOGIFcategory和kLOGIFdetailLTEQ的当前设置的情况下,

myLogLine(kLogVC, 2, @"%@",self);

将打印,但这不会

myLogLine(kLogGCD, 2, @"%@",self);//GCD not being printed

也不会

myLogLine(kLogGCD, 12, @"%@",self);//level too high

如果要覆盖单个日志呼叫的设置,请使用负级别:

myLogLine(kLogGCD, -2, @"%@",self);//now printed even tho' GCD category not active.

我发现输入每行的几个额外字符是值得的

  1. 打开或关闭整个注释类别(例如,仅报告标记为“模型”的那些呼叫)
  2. 报告具有较高级别数字的详细信息,或者仅报告标记有较低数字的最重要的呼叫

我敢肯定,很多人会觉得这有点过头了,但是以防万一有人发现它适合自己的目的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.