• 关于调试日志Log


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

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

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

    __func__%s 当前函数签名

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

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

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
        //连接xcode时可以从监视器中看日志 没连接时Log日志会输出到文件中,
        [self redirectNSLogToDocumentFolder];
        return YES;
    }
    
    - (void)redirectNSLogToDocumentFolder
    {
        //如果已经连接Xcode调试则不输出到文件
    //    if(isatty(STDOUT_FILENO)) {
    //        return;
    //    }
        
    //    UIDevice *device = [UIDevice currentDevice];
    //    if([[device model] hasSuffix:@"Simulator"]){ //在模拟器不保存到文件中
    //        return;
    //    }
        
        //将NSlog打印信息保存到Document目录下的Log文件夹下
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *logDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Log"];
        
        NSFileManager *fileManager = [NSFileManager defaultManager];
        BOOL fileExists = [fileManager fileExistsAtPath:logDirectory];
        if (!fileExists) {
            [fileManager createDirectoryAtPath:logDirectory  withIntermediateDirectories:YES attributes:nil error:nil];
        }
        
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
        [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; //每次启动后都保存一个新的日志文件中
        NSString *dateStr = [formatter stringFromDate:[NSDate date]];
        NSString *logFilePath = [logDirectory stringByAppendingFormat:@"/%@.log",dateStr];
        
        // 将log输入到文件
        freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
        freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
        
        //未捕获的Objective-C异常日志
        NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);
    }
    
    void UncaughtExceptionHandler(NSException* exception)
    {
        NSString* name = [ exception name ];
        NSString* reason = [ exception reason ];
        NSArray* symbols = [ exception callStackSymbols ]; // 异常发生时的调用栈
        NSMutableString* strSymbols = [ [ NSMutableString alloc ] init ]; //将调用栈拼成输出日志的字符串
        for ( NSString* item in symbols )
        {
            [ strSymbols appendString: item ];
            [ strSymbols appendString: @"
    " ];
        }
        
        //将crash日志保存到Document目录下的Log文件夹下
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *logDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Log"];
        
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if (![fileManager fileExistsAtPath:logDirectory]) {
            [fileManager createDirectoryAtPath:logDirectory  withIntermediateDirectories:YES attributes:nil error:nil];
        }
        
        NSString *logFilePath = [logDirectory stringByAppendingPathComponent:@"UncaughtException.log"];
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
        [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
        NSString *dateStr = [formatter stringFromDate:[NSDate date]];
        
        NSString *crashString = [NSString stringWithFormat:@"<- %@ ->[ Uncaught Exception ]
    Name: %@, Reason: %@
    [ Fe Symbols Start ]
    %@[ Fe Symbols End ]
    
    ", dateStr, name, reason, strSymbols];
        //把错误日志写到文件中
        if (![fileManager fileExistsAtPath:logFilePath]) {
            [crashString writeToFile:logFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
        }else{
            NSFileHandle *outFile = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
            [outFile seekToEndOfFile];
            [outFile writeData:[crashString dataUsingEncoding:NSUTF8StringEncoding]];
            [outFile closeFile];
        }
        
        //把错误日志发送到邮箱
    //    NSMutableString *mailUrl = [NSMutableString string];
    //    [mailUrl appendString:@"mailto:xxxxxxxxx@qq.com"];
    //    [mailUrl appendString:@"?subject=程序异常崩溃,请配合发送异常报告,谢谢合作!"];
    //    [mailUrl appendFormat:@"&body=%@", crashString];
    //    // 打开地址
    ////    NSString *mailPath = [mailUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    //    NSString *mailPath = [mailUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithRange:NSMakeRange(0, mailUrl.length)]];
    //    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];
    }
    日志code
    在代码中使用Autolayout时,大家都会使用NSDictionaryOfVariableBindings这个宏,这个宏可以生成一个变量名到变量值映射的Dictionary。比如NSDictionaryOfVariableBindings(button1, button2)将会生成一个{ @"button1" = button1, @"button2 = button2 }的Dictionary。它是怎么做到的呢?我们来看看这个宏的定义:
    
    #define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
    这个宏定义中有3个参数,后两个参数不难理解,但第一个参数中间有个#符号,语法上看起来比较怪异,这个是什么呢?以前在做越狱的mobilesubstrate开发时,其中定义的一堆宏频繁使用了这个符号,下面就来揭开#这个符号在宏定义中的迷雾。
    
    预编译的一些知识
    
    我们的代码在build时并不是直接进行编译的,在编译之前还进行了预编译处理。预编译会把include或import的文件导入到文件中,同时会将代码中用到的宏进行替换。注意宏是直接在代码中替换成宏的定义的,如果有嵌套也会逐层替换。
    
    “#”指示一些预编译命令
    
    预编译命令一般都是以#开头的,比如#include、#import、#if等,在这里就不一一说明了,本文主要说明一下#在宏定义里面的一些作用。
    
    宏参数字符串化
    
    在一个参数前加一个#,预处理时将会变成这个参数名的字符串常量,即字符串化(stringify)。比如:
    
    #define GET_NAME(X) #X
    int a = 0;
    NSLog(@"%s",GET_NAME(a));      //output: "a"
    NSLog(@"%s",GET_NAME(a+3));    //output: "a+3"
    将会得到以下输出:
    
    a
    a+3
    可以看出#,将参数原样转换成字符串常量,如果参数是一个表达式,那么输出这个表达式的原样字符串常量。
    
    回头再看看NSDictionaryOfVariableBindings的定义:
    
    #define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
    如果这样生成两个button的映射:
    
    NSDictionaryOfVariableBindings(button1, button2);
    那么预编译时就会转换成:
    
    _NSDictionaryOfVariableBindings(@"""button1, button2", button1, button2, nil);
    由于两个常量字符串放在一起就是字符串常量串联,将变成两个字符串常量组合在一起的字符串常量,也就是上面是一个空字符串"""button1, button2"串联,所以上面的代码等价于:
    
    _NSDictionaryOfVariableBindings(@"button1, button2", button1, button2, nil);
    那么_NSDictionaryOfVariableBindings函数就可以将它的第一个参数按逗号,分割开作为key,后面就是各个key对应的值了。因此这段代码就创建了一个内容为{ @"button1" = button1, @"button2 = button2 }的Dictionary。
    
    命名的串联
    
    #在宏定义中的另一个作用就是用于命名的串联,用##就可以串联它左右两边的命名,比如以下代码:
    
    #define CONCAT(X, Y) X ## Y
    NSString *helloworld = @"Hello, world!";
    NSLog(@"%@",CONCAT(hello, world)); //output: "Hello, world"
    CONCAT(hello, world)实际被转换成helloworld。注意一下,因为宏是预编译阶段进行展开的,就是说在编译之前,因此代码中的hello和world即使没有定义其实也是没问题的,预编译处理后,这两个命名是不存在的。
    
    可选可变参数
    
    ##在宏定义中可以放在__VA_ARGS__之前表示可变参数可以为空,否则的话可变参数至少为一个了。
    
    #define MYLOG(format, ...) NSLog(format, ##__VA_ARGS__)
    MYLOG(@"Don't make an error!");
    上面代码中MLOG中只有一个参数,如果不加##,则MLOG至少需要两个参数,在Xcode里将会出现编译错误。
    #的用法
    //将十六进制的字符串转为十进制字符串
    + (NSString *)stringFromHexString:(NSString *)hexString { //
        
        char *myBuffer = (char *)malloc((int)[hexString length] / 2 + 1);
        bzero(myBuffer, [hexString length] / 2 + 1);
        for (int i = 0; i < [hexString length] - 1; i += 2) {
            unsigned int anInt;
            NSString * hexCharStr = [hexString substringWithRange:NSMakeRange(i, 2)];
            NSScanner * scanner = [[NSScanner alloc] initWithString:hexCharStr];
            [scanner scanHexInt:&anInt];
            myBuffer[i / 2] = (char)anInt;
        }
        NSString *unicodeString = [NSString stringWithCString:myBuffer encoding:4];
        NSLog(@"------字符串=======%@",unicodeString);
        return unicodeString; 
        
        
    }
    十六进制字符串转为十进制字符串(异常错误处理)

    链接:

    iOS 调试日志信息清晰化

    __VA_ARGS__用法(转)

    “#”的迷雾

    IOS写日志文件并保存到Documents

  • 相关阅读:
    Android error--No implementation found for native Lcomd
    【CareerCup】Trees and Graphs—Q4.3
    android 分享到QQ空间的全部操作
    ubuntu12.04安装翻译软件stardict及卸载
    Java获取当前操作系统的信息
    WebForm和MVC中都可以使用的路由
    使用 System.Transactions 进行事物管理
    .NET中四种常用事物
    sql语句:创建事物
    sql语句:CASE WHEN END 的用法
  • 原文地址:https://www.cnblogs.com/On1Key/p/5729625.html
Copyright © 2020-2023  润新知