iOS objc_msgSend 野指针Crash 从 Log 提取 Crash 时 selector 的地址和名字并打印
从 crash stack log 里面,提取 objc_msgSend 关键字,定位是否是野指针问题导致的crash,如果是则打印 crash 时的 objc_msgSend 调用的第二个参数,即 selector 的地址和名字String,方便定位和 fix 此类型的 crash。
具体提取方法:
1. 提取字符串地址:
32bit 机器,读取 crash 时 log 里面 r1: 0x 后面的 8 个 16 进制数字字符串;
64bit 机器,读取 crash 时 log 里面 x1: 0x 后面的 16 个 16 进制数字字符串;
2. 通过 strtoul 将字符串地址转化为无符号整型地址;
3. 同时打印 selector 的 “地址” 和 selector “字符串” 即 selectorName。
static void printLastSelectorName(NSString *crashStackString) { // print registers QZLOG_INFO(@"*** print registers begin. ***"); if (crashStackString.length > 0 && ([crashStackString rangeOfString:@"objc_msgSend"].location != NSNotFound)) { QZLOG_INFO(@"*** have found objc_msgSend. ***"); NSString *r1Flag = @"r1: 0x"; NSString *x1Flag = @"x1: 0x"; NSRange rangeContainsR1Reg = [crashStackString rangeOfString:r1Flag]; NSRange rangeContainsX1Reg = [crashStackString rangeOfString:x1Flag]; NSString *valOfR1X1 = nil; @try { if ((rangeContainsR1Reg.location != NSNotFound) && (crashStackString.length >= rangeContainsR1Reg.location + r1Flag.length + 8)) { // 32-bit valOfR1X1 = [crashStackString substringWithRange:NSMakeRange(rangeContainsR1Reg.location + r1Flag.length, 8)]; } else if ((rangeContainsX1Reg.location != NSNotFound) && (crashStackString.length >= rangeContainsX1Reg.location + x1Flag.length + 16)) { // 64-bit valOfR1X1 = [crashStackString substringWithRange:NSMakeRange(rangeContainsX1Reg.location + x1Flag.length, 16)]; } } @catch (NSException *exception) { QZLOG_ERROR(@"*** exception: %@", exception); } if (valOfR1X1.length > 0) { unsigned long val = strtoul([[valOfR1X1 substringWithRange:NSMakeRange(0, valOfR1X1.length)] UTF8String], 0, 16); if (val != 0 && val != ULONG_MAX) { QZLOG_INFO(@"*** r1(x1) val = %lx", val); QZLOG_INFO(@"*** r1(x1): %@", NSStringFromSelector((SEL)val)); } } } QZLOG_INFO(@"*** print registers end. ***"); }