Bloodline's Blog Notes and thoughts from Bloodline

屏蔽冗余日志信息(NSLog)

Comments

前言

打印了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_startva_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_startap指向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的信息,不过影响不大了。