NSRangeException异常求助:索引越界问题及相关代码排查
排查NSRangeException索引越界问题
这个问题我之前帮同事排查过,大概率是iOS基于WebKit的HTML文本解析器在处理RTL(从右到左)文本+特定字体时的内部bug,结合你的代码和异常信息,具体分析和解决方法如下:
问题原因分析
- iOS的
NSHTMLTextDocumentType解析器依赖WebKit内核,当处理RTL排版(比如你设置的direction: RTL)+Mehr Nastaliq Web这类波斯/乌尔都语字体时,对复杂连字、特殊Unicode字符(比如零宽空格、组合字符)的索引计算容易出现偏差,导致内部维护的字符串长度和实际字符数不匹配,触发characterAtIndex:的索引越界。 - 你使用的
NSUnicodeStringEncoding编码方式也可能加剧这个问题,WebKit对UTF-8编码的HTML支持更稳定,Unicode编码可能在解析时产生额外的字符处理逻辑错误。
可行的解决方法
1. 更换HTML字符串编码为UTF-8
这是最容易尝试的修复方式,把NSUnicodeStringEncoding换成NSUTF8StringEncoding,很多时候能直接解决解析时的索引计算问题:
NSString *htmlString = [NSString stringWithFormat:@"<html> <head><style type=\"text/css\">body { font-family: Mehr Nastaliq Web; font-size: 22pt; white-space: pre-wrap; text-align: right; lang: ar; direction: RTL; -webkit-user-select: none; }</style> </head><body leftmargin=\"20\" topmargin=\"0\" rightmargin=\"20\" > %@ </body></html>",str]; // 改用UTF-8编码 NSData *htmlData = [htmlString dataUsingEncoding:NSUTF8StringEncoding]; NSAttributedString *attributedString = [[NSAttributedString alloc] initWithData:htmlData options: @{ NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType } documentAttributes: nil error: nil]; self.txtView.attributedText=attributedString;
2. 预处理字符串,清理特殊字符
如果你的str中包含零宽空格、不可见控制字符或者异常Unicode组合字符,先过滤掉这些字符能减少解析器的出错概率:
// 移除零宽空格、控制字符等可能干扰解析的特殊字符 NSString *cleanedStr = [str stringByReplacingOccurrencesOfString:@"[\u200B-\u200F\uFEFF\u0000-\u001F]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, str.length)]; // 用清理后的字符串生成HTML NSString *htmlString = [NSString stringWithFormat:@"<html> <head><style type=\"text/css\">body { font-family: Mehr Nastaliq Web; font-size: 22pt; white-space: pre-wrap; text-align: right; lang: ar; direction: RTL; -webkit-user-select: none; }</style> </head><body leftmargin=\"20\" topmargin=\"0\" rightmargin=\"20\" > %@ </body></html>",cleanedStr];
3. 绕开HTML解析,直接用NSAttributedString设置排版属性
如果HTML解析的bug难以规避,直接用原生的NSAttributedString属性来实现RTL排版和字体设置,完全绕开WebKit解析:
// 创建可变属性字符串 NSMutableAttributedString *mutableAttrStr = [[NSMutableAttributedString alloc] initWithString:str]; // 设置RTL段落样式 NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft; paragraphStyle.alignment = NSTextAlignmentRight; paragraphStyle.firstLineHeadIndent = 20; paragraphStyle.tailIndent = -20; [mutableAttrStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, mutableAttrStr.length)]; // 设置字体和禁止选中属性 UIFont *nastaliqFont = [UIFont fontWithName:@"Mehr Nastaliq Web" size:22.0]; [mutableAttrStr addAttribute:NSFontAttributeName value:nastaliqFont range:NSMakeRange(0, mutableAttrStr.length)]; [mutableAttrStr addAttribute:NSUserInterfaceItemIdentifierAttributeName value:@"-webkit-user-select: none" range:NSMakeRange(0, mutableAttrStr.length)]; self.txtView.attributedText = mutableAttrStr;
4. 添加异常捕获作为兜底方案
如果以上方法都无法完全避免崩溃,可以添加异常捕获逻辑,当解析失败时回退到普通字符串显示,保证App不崩溃:
NSString *htmlString = [NSString stringWithFormat:@"<html> <head><style type=\"text/css\">body { font-family: Mehr Nastaliq Web; font-size: 22pt; white-space: pre-wrap; text-align: right; lang: ar; direction: RTL; -webkit-user-select: none; }</style> </head><body leftmargin=\"20\" topmargin=\"0\" rightmargin=\"20\" > %@ </body></html>",str]; NSAttributedString *attributedString; @try { NSData *htmlData = [htmlString dataUsingEncoding:NSUTF8StringEncoding]; attributedString = [[NSAttributedString alloc] initWithData:htmlData options: @{ NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType } documentAttributes: nil error: nil]; } @catch (NSException *exception) { NSLog(@"HTML解析触发异常: %@", exception); // 回退到普通字符串 attributedString = [[NSAttributedString alloc] initWithString:str]; } self.txtView.attributedText = attributedString;
内容的提问来源于stack exchange,提问作者jaydev
相关产品推荐
相关产品推荐

