Objective-C从NSArray读取十六进制值返回0及崩溃问题排查
问题根本原因
你遇到的三个现象本质是两个核心问题导致的:
- 崩溃问题:
NSString没有名为unsignedLongValue的公开实例方法,你调用不存在的方法触发了找不到选择器的异常,报错信息中提到的lu是栈上NSLog的格式化占位符%lu被异常解析逻辑误捕获导致的,和格式化本身无关。 - 数值解析错误问题:NSString 自带的所有数值转换方法(包括
intValue/unsignedIntValue/longLongValue等)默认只按十进制规则解析字符串,遇到非0-9的字符会立刻终止解析。你存入数组的是带a-f字母的十六进制字符串,比如@"FF"第一个字符就是非数字的F,解析直接终止返回0;@"7b"解析到b就停止,只返回前面的7,和你观察到的现象完全吻合。 - 性能顾虑问题:你担心
strtoul性能差是完全多余的,它是轻量C标准库函数,没有Objective-C消息发送开销,实际性能并不比NSString自带的十进制转换方法差。但你当前的实现从根源上就做了多余的字符串转换,完全可以做到零解析开销。
低开销修复方案
最优方案:从读取逻辑入手,直接存数值而非字符串
你本来就是逐字节读取文件二进制内容,完全没必要把每个字节转成十六进制字符串再存数组,直接存字节对应的NSNumber对象即可,后续读取不需要任何解析步骤,性能拉满:
NSBundle *mainBundle = [NSBundle mainBundle]; NSFileHandle *fh; NSData *wordBuffer; NSMutableArray<NSNumber *> *myByteArray = [NSMutableArray new]; NSString *filePath = [mainBundle pathForResource:@"data" ofType:@"txt"]; fh = [NSFileHandle fileHandleForReadingAtPath: filePath]; while ((wordBuffer = [fh readDataOfLength:1]) && [wordBuffer length] ) { // 直接读取当前字节的数值存入数组,不需要转十六进制字符串 uint8_t byte = *(const uint8_t *)wordBuffer.bytes; [myByteArray addObject:@(byte)]; } [fh closeFile]; return myByteArray;
后续读取数值时直接调用对应方法即可,没有任何转换开销:
uint8_t test = [response[1] unsignedCharValue]; NSLog(@"Value: %u", test);
兼容方案:必须保留字符串存储时的高性能转换
如果因为业务兼容要求必须存十六进制字符串,strtoul已经是性能足够高的选择。如果要进一步压缩开销,可以写一个专门处理单字节双字符十六进制的内联转换函数,比通用strtoul少了很多冗余判断,性能更高:
static inline uint8_t convertHexStrToByte(NSString *hexStr) { const char *chars = hexStr.UTF8String; uint8_t result = 0; for (int i = 0; i < 2 && chars[i] != '\0'; i++) { char c = chars[i]; result <<= 4; if (c >= '0' && c <= '9') { result += c - '0'; } else if (c >= 'a' && c <= 'f') { result += 10 + c - 'a'; } else if (c >= 'A' && c <= 'F') { result += 10 + c - 'A'; } } return result; } // 调用示例 uint8_t test = convertHexStrToByte(response[1]);
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

