__VA_ARGS__  是一个可变参数的宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错

__FILE__  %s   宏在预编译时会替换成当前的源文件名,当前源代码文件全路径

__FUNCTION__宏在预编译时会替换成当前的函数名称

__func__%s 当前函数签名

__LINE__ %d 在源代码文件中当前所在行数,宏在预编译时会替换成当前的行号

__PRETTY_FUNCTION__ %s 像 __func__,但是包含了C++代码中的隐形类型信息

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
  2. //连接xcode时可以从监视器中看日志 没连接时Log日志会输出到文件中,
  3. [self redirectNSLogToDocumentFolder];
  4. return YES;
  5. }
  6.  
  7. - (void)redirectNSLogToDocumentFolder
  8. {
  9. //如果已经连接Xcode调试则不输出到文件
  10. // if(isatty(STDOUT_FILENO)) {
  11. // return;
  12. // }
  13.  
  14. // UIDevice *device = [UIDevice currentDevice];
  15. // if([[device model] hasSuffix:@"Simulator"]){ //在模拟器不保存到文件中
  16. // return;
  17. // }
  18.  
  19. //将NSlog打印信息保存到Document目录下的Log文件夹下
  20. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  21. NSString *logDirectory = [[paths objectAtIndex:] stringByAppendingPathComponent:@"Log"];
  22.  
  23. NSFileManager *fileManager = [NSFileManager defaultManager];
  24. BOOL fileExists = [fileManager fileExistsAtPath:logDirectory];
  25. if (!fileExists) {
  26. [fileManager createDirectoryAtPath:logDirectory withIntermediateDirectories:YES attributes:nil error:nil];
  27. }
  28.  
  29. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  30. [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
  31. [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; //每次启动后都保存一个新的日志文件中
  32. NSString *dateStr = [formatter stringFromDate:[NSDate date]];
  33. NSString *logFilePath = [logDirectory stringByAppendingFormat:@"/%@.log",dateStr];
  34.  
  35. // 将log输入到文件
  36. freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
  37. freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
  38.  
  39. //未捕获的Objective-C异常日志
  40. NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);
  41. }
  42.  
  43. void UncaughtExceptionHandler(NSException* exception)
  44. {
  45. NSString* name = [ exception name ];
  46. NSString* reason = [ exception reason ];
  47. NSArray* symbols = [ exception callStackSymbols ]; // 异常发生时的调用栈
  48. NSMutableString* strSymbols = [ [ NSMutableString alloc ] init ]; //将调用栈拼成输出日志的字符串
  49. for ( NSString* item in symbols )
  50. {
  51. [ strSymbols appendString: item ];
  52. [ strSymbols appendString: @"\r\n" ];
  53. }
  54.  
  55. //将crash日志保存到Document目录下的Log文件夹下
  56. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  57. NSString *logDirectory = [[paths objectAtIndex:] stringByAppendingPathComponent:@"Log"];
  58.  
  59. NSFileManager *fileManager = [NSFileManager defaultManager];
  60. if (![fileManager fileExistsAtPath:logDirectory]) {
  61. [fileManager createDirectoryAtPath:logDirectory withIntermediateDirectories:YES attributes:nil error:nil];
  62. }
  63.  
  64. NSString *logFilePath = [logDirectory stringByAppendingPathComponent:@"UncaughtException.log"];
  65. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  66. [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
  67. [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  68. NSString *dateStr = [formatter stringFromDate:[NSDate date]];
  69.  
  70. NSString *crashString = [NSString stringWithFormat:@"<- %@ ->[ Uncaught Exception ]\r\nName: %@, Reason: %@\r\n[ Fe Symbols Start ]\r\n%@[ Fe Symbols End ]\r\n\r\n", dateStr, name, reason, strSymbols];
  71. //把错误日志写到文件中
  72. if (![fileManager fileExistsAtPath:logFilePath]) {
  73. [crashString writeToFile:logFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
  74. }else{
  75. NSFileHandle *outFile = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
  76. [outFile seekToEndOfFile];
  77. [outFile writeData:[crashString dataUsingEncoding:NSUTF8StringEncoding]];
  78. [outFile closeFile];
  79. }
  80.  
  81. //把错误日志发送到邮箱
  82. // NSMutableString *mailUrl = [NSMutableString string];
  83. // [mailUrl appendString:@"mailto:xxxxxxxxx@qq.com"];
  84. // [mailUrl appendString:@"?subject=程序异常崩溃,请配合发送异常报告,谢谢合作!"];
  85. // [mailUrl appendFormat:@"&body=%@", crashString];
  86. // // 打开地址
  87. //// NSString *mailPath = [mailUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  88. // NSString *mailPath = [mailUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithRange:NSMakeRange(0, mailUrl.length)]];
  89. // [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];
  90. }

日志code

  1. 在代码中使用Autolayout时,大家都会使用NSDictionaryOfVariableBindings这个宏,这个宏可以生成一个变量名到变量值映射的Dictionary。比如NSDictionaryOfVariableBindings(button1, button2)将会生成一个{ @"button1" = button1, @"button2 = button2 }的Dictionary。它是怎么做到的呢?我们来看看这个宏的定义:
  2.  
  3. #define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
  4. 这个宏定义中有3个参数,后两个参数不难理解,但第一个参数中间有个#符号,语法上看起来比较怪异,这个是什么呢?以前在做越狱的mobilesubstrate开发时,其中定义的一堆宏频繁使用了这个符号,下面就来揭开#这个符号在宏定义中的迷雾。
  5.  
  6. 预编译的一些知识
  7.  
  8. 我们的代码在build时并不是直接进行编译的,在编译之前还进行了预编译处理。预编译会把include或import的文件导入到文件中,同时会将代码中用到的宏进行替换。注意宏是直接在代码中替换成宏的定义的,如果有嵌套也会逐层替换。
  9.  
  10. “#”指示一些预编译命令
  11.  
  12. 预编译命令一般都是以#开头的,比如#include、#import、#if等,在这里就不一一说明了,本文主要说明一下#在宏定义里面的一些作用。
  13.  
  14. 宏参数字符串化
  15.  
  16. 在一个参数前加一个#,预处理时将会变成这个参数名的字符串常量,即字符串化(stringify)。比如:
  17.  
  18. #define GET_NAME(X) #X
  19. int a = 0;
  20. NSLog(@"%s",GET_NAME(a)); //output: "a"
  21. NSLog(@"%s",GET_NAME(a+)); //output: "a+3"
  22. 将会得到以下输出:
  23.  
  24. a
  25. a+
  26. 可以看出#,将参数原样转换成字符串常量,如果参数是一个表达式,那么输出这个表达式的原样字符串常量。
  27.  
  28. 回头再看看NSDictionaryOfVariableBindings的定义:
  29.  
  30. #define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
  31. 如果这样生成两个button的映射:
  32.  
  33. NSDictionaryOfVariableBindings(button1, button2);
  34. 那么预编译时就会转换成:
  35.  
  36. _NSDictionaryOfVariableBindings(@"""button1, button2", button1, button2, nil);
  37. 由于两个常量字符串放在一起就是字符串常量串联,将变成两个字符串常量组合在一起的字符串常量,也就是上面是一个空字符串""和"button1, button2"串联,所以上面的代码等价于:
  38.  
  39. _NSDictionaryOfVariableBindings(@"button1, button2", button1, button2, nil);
  40. 那么_NSDictionaryOfVariableBindings函数就可以将它的第一个参数按逗号,分割开作为key,后面就是各个key对应的值了。因此这段代码就创建了一个内容为{ @"button1" = button1, @"button2 = button2 }的Dictionary
  41.  
  42. 命名的串联
  43.  
  44. #在宏定义中的另一个作用就是用于命名的串联,用##就可以串联它左右两边的命名,比如以下代码:
  45.  
  46. #define CONCAT(X, Y) X ## Y
  47. NSString *helloworld = @"Hello, world!";
  48. NSLog(@"%@",CONCAT(hello, world)); //output: "Hello, world"
  49. CONCAT(hello, world)实际被转换成helloworld。注意一下,因为宏是预编译阶段进行展开的,就是说在编译之前,因此代码中的helloworld即使没有定义其实也是没问题的,预编译处理后,这两个命名是不存在的。
  50.  
  51. 可选可变参数
  52.  
  53. ##在宏定义中可以放在__VA_ARGS__之前表示可变参数可以为空,否则的话可变参数至少为一个了。
  54.  
  55. #define MYLOG(format, ...) NSLog(format, ##__VA_ARGS__)
  56. MYLOG(@"Don't make an error!");
  57. 上面代码中MLOG中只有一个参数,如果不加##,则MLOG至少需要两个参数,在Xcode里将会出现编译错误。

#的用法

  1. //将十六进制的字符串转为十进制字符串
  2. + (NSString *)stringFromHexString:(NSString *)hexString { //
  3.  
  4. char *myBuffer = (char *)malloc((int)[hexString length] / + );
  5. bzero(myBuffer, [hexString length] / + );
  6. for (int i = ; i < [hexString length] - ; i += ) {
  7. unsigned int anInt;
  8. NSString * hexCharStr = [hexString substringWithRange:NSMakeRange(i, )];
  9. NSScanner * scanner = [[NSScanner alloc] initWithString:hexCharStr];
  10. [scanner scanHexInt:&anInt];
  11. myBuffer[i / ] = (char)anInt;
  12. }
  13. NSString *unicodeString = [NSString stringWithCString:myBuffer encoding:];
  14. NSLog(@"------字符串=======%@",unicodeString);
  15. return unicodeString;
  16.  
  17. }

十六进制字符串转为十进制字符串(异常错误处理)

链接:

iOS 调试日志信息清晰化

__VA_ARGS__用法(转)

“#”的迷雾

IOS写日志文件并保存到Documents

关于调试日志Log的更多相关文章

  1. JMeter-显示调试日志log

    JMeter-调试日志记录 参考文档:https://jmeter.apache.org/usermanual/hints_and_tips.html 大多数测试元素包括调试日志记录. 如果从GUI运 ...

  2. Expo大作战(六)--expo开发模式,expo中exp命令行工具,expo中如何查看日志log,expo中的调试方式

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,将全部来与官网 我猜去全部机翻+个人 ...

  3. 如何正确使用日志Log

    title: 如何正确使用日志Log date: 2015-01-08 12:54:46 categories: [Python] tags: [Python,log] --- 文章首发地址:http ...

  4. paip.提升效率--调试--日志系统日志参数含义---python

    paip.提升效率--调试--日志系统日志参数含义---python #同时向控制台和文件输出日志 #日志参数含义 import logging log_format = '%(filename)s ...

  5. 【写一个自己的js库】 2.实现自己的调试日志

    还是本着学习的目的,实现一个自己的调试日志,界面很简单,就是将调试信息显示在页面的正中央,用一个ul包裹,每条信息就是一个li. 1.新建一个myLogger.js文件,将需要的方法声明一下.其中va ...

  6. Android开发调试日志工具类[支持保存到SD卡]

    直接上代码: package com.example.callstatus; import java.io.File; import java.io.FileWriter; import java.i ...

  7. nginx 学习笔记(5) nginx调试日志

    为启动一个调试日志,nginx需要在构建时配置城支持调试模式. ./configure --with-debug ... 而且调试级别应该使用err_log指令来设置: err_log /path/t ...

  8. 在Lua中封装一个调试日志(附lua时间格式)

    --自己封装一个Debug调试日志 Debug={} Info={} local function writeMsgToFile(filepath,msg) end function Debug.Lo ...

  9. Android输出日志Log类并保存到文件中

    android.util.Log常用的方法有以下5个: Log.v() Log.d() Log.i() Log.w() 以及 Log.e().根据首字母分别对应VERBOSE,DEBUG,INFO,W ...

随机推荐

  1. linker command failed with exit code 1 (use -v to see invocation)解决办法

    [cpp] view plaincopy Undefined symbols for architecture i386:     "_OBJC_CLASS_$_FMDatabase&quo ...

  2. runtime之玩转成员变量

    前言: 不铺垫那么多,单刀直入吧:runtime是一个C和汇编写的动态库,就像是一个小小的系统,将OC和C紧密关联在一次,这个系统主要做两件事情. 1,封装C语言的结构体和函数,让开发者在运行时创建, ...

  3. JMS(Java消息服务)入门教程

    什么是Java消息服务 Java消息服务指的是两个应用程序之间进行异步通信的API,它为标准消息协议和消息服务提供了一组通用接口,包括创建.发送.读取消息等,用于支持JAVA应用程序开发.在J2EE中 ...

  4. substring -----截取字符串

    var str = "0123456789"; substring alert(str.substring(0));------------"0123456789&quo ...

  5. java.lang.NoClassDefFoundError:TagSupport

    这个错误应该就是没有成功加载tomcat自带的jar包jsp-api.jar. 在网上看到很多网友说要把tomcat/lib下的jsp-api.jar拷贝到项目/WEB_INF/lib下并导入,本人试 ...

  6. mysql-6 数据检索(4)

    汇总数据 函数 说明 AVG() 返回某列的平均数 COUNT() 返回某列的行数 MAX() 返回某列的最大值 MIN() 返回某列的最小值 SUM() 返回某列值的和 1.AVG函数 SELECT ...

  7. Java常用排序算法+程序员必须掌握的8大排序算法+二分法查找法

    Java 常用排序算法/程序员必须掌握的 8大排序算法 本文由网络资料整理转载而来,如有问题,欢迎指正! 分类: 1)插入排序(直接插入排序.希尔排序) 2)交换排序(冒泡排序.快速排序) 3)选择排 ...

  8. MySQL出现Waiting for table metadata lock的原因以及解决方法

    转自:http://ctripmysqldba.iteye.com/blog/1938150 (有修改) MySQL在进行alter table等DDL操作时,有时会出现Waiting for tab ...

  9. PHP加密3DES报错 Call to undefined function: mcrypt_module_open() 的解决方法

    我也是PHP新手,通过w3cschool了解了一下php基本原理之后就开写了.但仍是菜鸟. 先不管3DES加密的方法对不对,方法都是网上的,在运行的时候报了个错,把小弟整死了.找来找去终于自己摸出了方 ...

  10. 给锁住的行解锁(oracle)

    1.查看数据库锁,诊断锁的来源及类型: select object_id,session_id,locked_mode from v$locked_object; 或者用以下命令: select b. ...