How to print out the method name and line number and conditionally disable NSLog?
我正在Xcode中做一个关于调试的演示,希望获得有关有效使用nslog的更多信息。
特别是,我有两个问题:
- 有没有一种方法可以轻松地nslog当前方法的名称/行号?
- 在编译发布代码之前,是否有一种方法可以轻松地"禁用"所有nslog?
下面是一些关于nslog的有用宏,我经常使用:
1 2 3 4 5 6 7 8 | #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宏仅用于在设置调试变量时输出(调试配置的项目C标志中有-ddebug)。
ALOG将始终输出文本(如常规nslog)。
输出(例如alog(@"Hello World"))如下:
1 | -[LibraryController awakeFromNib] [Line 364] Hello world |
我从上面取了
总结:
- 只有在设置调试变量时,
DLog 才会像NSLog 那样输出。 ALog 总是像NSLog 那样输出。- 只有在设置了调试变量时,
ULog 才会显示UIAlertView 。< BR>
1 2 3 4 5 6 7 8 9 10 11 12 | #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 [Line %d]", __PRETTY_FUNCTION__, __LINE__] message:[NSString stringWithFormat:fmt, ##__VA_ARGS__] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; } #else # define ULog(...) #endif |
这就是它的样子:
+ 1迪埃德里克
1 | NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__); |
输出文件名、行号和函数名:
1 | /proj/cocoa/cdcli/cdcli.m 121 managedObjectContext managedObjectContext |
C++中的EDOCX1 0表示有名称的EDOCX1,1表示良好的函数名,在可可中看起来相同。
我不确定禁用nslog的正确方法是什么,我做到了:
1 | #define NSLog |
没有日志输出出现,但是我不知道这是否有任何副作用。
这里有一个我们使用的调试常量的大集合。享受。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | // 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 |
有一个没有答案的新把戏。你可以用
用
1 | 2011-11-03 13:43:55.632 myApp[3739:207] Hello Word |
但是有了
1 | Hello World |
使用此代码
1 2 3 4 5 6 | #ifdef DEBUG #define NSLog(FORMAT, ...) fprintf(stderr,"%s ", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]); #else #define NSLog(...) {} #endif |
我对这个问题的回答可能会有所帮助,看起来就像迪德里克做的那样。您还可能希望用自己的自定义日志类的静态实例替换对
1 2 3 4 5 6 7 8 9 10 11 | #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 |
禁用所有nslog,对于对宏过敏的人,您也可以编译以下内容:
1 2 3 4 5 6 7 8 9 10 |
而且,使用它就像nslog一样:
1 | SJLog(@"bye bye NSLogs !"); |
从这个博客:http://whackylabs.com/rants/?P=134
可以很容易地将现有nslog更改为显示从中调用它们的行号和类。在前缀文件中添加一行代码:
1 | #define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d]" __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) |
为了补充以上的答案,在某些情况下,特别是在调试时,使用nslog替换可能非常有用。例如,去掉每一行上的所有日期和进程名/id信息可以使输出更可读、更快速地引导。
下面的链接提供了很多有用的弹药,可以使简单的日志记录变得更好。
http://cocaheads.byu.edu/wiki/a-different-nslog
例如,这很简单
-(void)applicationWillEnterForeground:(UIApplication *)application {
1 NSLog(@"%s", __PRETTY_FUNCTION__);}
输出:-[应用程序委派应用程序将进入前台:]
在以上答案的基础上,下面是我剽窃并提出的问题。还添加了内存日志记录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #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 |
新添加到DLOG。不要完全从已发布的应用程序中删除调试,只需禁用它。当用户遇到需要调试的问题时,只需说明如何在已发布的应用程序中启用调试,并通过电子邮件请求日志数据。
短版本:创建全局变量(是的,懒惰和简单的解决方案),然后这样修改dlog:
1 2 | BOOL myDebugEnabled = FALSE; #define DLog(fmt, ...) if (myDebugEnabled) NSLog((@"%s [Line %d]" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); |
在Jomnius-Ilesson-Ilearned上给出了更长的答案:如何在发布的应用程序中进行动态调试登录
有一段时间,我一直在使用一个从上面几个采用宏的网站。我的重点是在控制台中登录,重点是控制和过滤的详细信息;如果您不介意很多日志行,但希望轻松地打开和关闭它们的批,那么您可能会发现这很有用。
首先,我可以选择用上面@rodrigo描述的printf替换nslog。
1 2 3 4 5 6 | #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 ", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]); #endif |
接下来,我打开或关闭登录。
1 2 3 | #ifdef DEBUG #define LOG_CATEGORY_DETAIL// comment out to turn all conditional logging off while keeping other DEBUG features #endif |
在主块中,定义与应用程序中的模块对应的各种类别。还要定义一个日志记录级别,超过该级别将不调用日志记录调用。然后定义nslog输出的各种味道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #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和klogifdetailteq的当前设置,调用
1 | myLogLine(kLogVC, 2, @"%@",self); |
会打印但不会
1 | myLogLine(kLogGCD, 2, @"%@",self);//GCD not being printed |
也不会
1 | myLogLine(kLogGCD, 12, @"%@",self);//level too high |
如果要覆盖单个日志调用的设置,请使用负级别:
1 | myLogLine(kLogGCD, -2, @"%@",self);//now printed even tho' GCD category not active. |
我发现每行输入的几个额外字符都是值得的。
我相信很多人会觉得这有点过分,但只是以防万一,有人发现这符合他们的目的。