屏蔽冗余日志信息(NSLog)
09 Sep 2016
前言
打印了scrollView
的代理方法,查看调用顺序,却发现满屏的无关日志。实在忍不了。
关于NSLog
NSLog
是这么个东西:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
FOUNDATION_EXPORT void NSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,0);
一般情况下,为了在生产环境下屏蔽调试日志,我们会用一个NSLog
宏来代替:
#ifdef DEBUG
#define MyLog(format, ...) NSLog(format, ## __VA_ARGS__)
#else
#define MyLog(format, ...)
#endif
或更详细的:
#ifdef DEBUG
# define MyLog(fmt, ...) NSLog((@"[文件名:%s]\n" "[函数名:%s]\n" "[行号:%d] \n" fmt), __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
# define MyLog(...);
#endif
__VA_ARGS__
是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的”,”去掉的作用,否则会编译出错。
__FILE__
宏在预编译时会替换成当前的源文件名。
__LINE__
宏在预编译时会替换成当前的行号。
__FUNCTION__
宏在预编译时会替换成当前的函数名称。
替换NSLog
//...是省略参数的宏的写法,后面的__VA_ARGS__是系统定义好的一个宏,来声明不定参数
#define NSLog(...) MyLog(__VA_ARGS__)
//实现打印
void MyLog(NSString *str,...){
va_list ap;
va_start(ap, str);
int flag = va_arg(ap, int);
if (flag == -1) {
NSLogv(str, ap);
}
va_end(ap);
}
关于va_start
和va_end
由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦。在进程中,堆栈地址是从高到低分配的。当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减(一些黑客就是在堆栈中修改函数返回地址,执行自己的代码来达到执行自己插入的代码段的目的)。看代码:
func( Type para1, Type para2, Type para3, ... )
{
/****** Step 1 ******/
va_list ap;
va_start( ap, para3 ); //第二个参数一定是...之前的参数。
/****** Step 2 ******/
//此时ap指向第一个可变参数
//调用va_arg取得里面的值
Type xx = va_arg( ap, Type );
//Type一定要相同,如:
//char *p = va_arg( ap, char *);
//int i = va_arg( ap, int );
//如果有多个参数继续调用va_arg
/****** Step 3 ******/
va_end(ap); //For robust!
}
说人话:上面的代码中,va_start
用ap
指向para3
之后的参数(实际上,va_start
的第二个参数也必须是...
之前的参数,才能实现取出可变参数),于是ap
就指向了第一个可变参数。然后,用va_arg()
取得类型t的可变参数值, 先是让ap
指向下一个参数。最后,用va_end(ap)
,给ap
初始化,保持健壮性。所以在使用时多加个参数-1:NSLog(@"%@", -1, @"tableView");
,这样就会打印出来了。没有加参数-1的调用会被过滤掉。
虽然在测试项目中OK,但是运用到项目中总是出现错误。整了两天也没弄好。这条路子看来是断了。
宏替换
最后修改prefix
文件中NSLog
的宏,用了整体宏替换的方式,以_
为前缀的日志打印才能够打印出来。
#ifndef NSLogKey
#define NSLogKey @"_"
#endif
#ifndef __OPTIMIZE__
# define NSLog(s, ...) {if ([s hasPrefix:NSLogKey]) {NSLog( @"%@:%d %@", [NSString stringWithUTF8String:__PRETTY_FUNCTION__], __LINE__,[[NSString stringWithFormat:(s), ##__VA_ARGS__] substringFromIndex:1]);}}
#else
# define NSLog(...) {}
#endif
其他
如果项目中用到了DDlog
,则把[DDLog addLogger:[DDTTYLogger sharedInstance]];
注释掉就好了。现在LLDB的输出基本没有了,可能还会有些第三方SDK的信息,不过影响不大了。