如何修复调用[NSString availableStringEncodings]时产生的内存泄漏问题
泄漏原因
你观测到的内存泄漏并非[NSString availableStringEncodings]方法本身导致,核心问题出在遍历编码的循环逻辑:
- 循环内调用
+[NSString stringWithContentsOfFile:encoding:error:]生成的NSString、NSError对象都是autorelease类型,会暂时缓存在当前线程的autorelease池中,直到池销毁才会释放 - 遍历所有可用编码的逻辑会执行数十次循环,如果待读取的文件体积偏大,大量临时autorelease对象会在循环过程中累积,就会被性能检测工具识别为内存泄漏/内存峰值异常
修复方案
方案1:最小改动修复
只需要在循环内部加局部autoreleasepool,每次循环结束立即释放本次产生的临时对象即可:
- (NSString *)stringFromArchive:(NSString *)fileName inDirectory:(NSString *)folderName { NSString *rStr = @""; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:folderName]; NSString *filePath = [folderPath stringByAppendingPathComponent:fileName]; NSFileManager * fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:filePath]) { NSError *error; const NSStringEncoding *encodings = [NSString availableStringEncodings]; NSStringEncoding encoding1; while ((encoding1 = *encodings++) != 0) { @autoreleasepool { // 新增局部自动释放池 rStr = [NSString stringWithContentsOfFile:filePath encoding:encoding1 error:&error]; if (rStr == nil) { NSLog(@"error: %@", error); }else{ break; } } } } return rStr; }
方案2:优化版修复(推荐)
当前遍历全量编码的逻辑效率极低,苹果已提供自动检测文件编码的原生API,无需手动遍历,从根源上避免了多次读取文件产生的临时内存:
- (NSString *)stringFromArchive:(NSString *)fileName inDirectory:(NSString *)folderName { NSString *rStr = @""; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *folderPath = [documentsDirectory stringByAppendingPathComponent:folderName]; NSString *filePath = [folderPath stringByAppendingPathComponent:fileName]; NSFileManager * fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:filePath]) { NSError *error; NSStringEncoding usedEncoding; // 系统自动检测编码,仅读取一次文件 rStr = [NSString stringWithContentsOfFile:filePath usedEncoding:&usedEncoding error:&error]; if (rStr == nil) { NSLog(@"error: %@", error); } } return rStr; }
如果有特殊场景需要优先匹配指定编码,可以先自定义常用编码列表(比如UTF8、GB18030、Unicode等)逐个尝试,匹配失败再调用自动检测接口,性能远高于遍历全量编码。
内容的提问来源于stack exchange,提问作者youngmama
相关产品推荐
相关产品推荐

