ios - 解码一个巨大的 NSString,内存不足

标签 ios xml memory-management base64 nsdata

我正在寻找有关如何改进使用 base64 编码解码 40+MB NSString 并将其保存到文件的过程的想法,同时能够将该过程放入 iPad 1 的 256 MB RAM

我从 NSXMLParser 得到 NSString:

id pointerToString;

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:@"myElement"]) 
    {
    pointerToString = [string retain];
}
}

然后我在回调中使用 pointerToString:
[handler performSelector: action withObject: pointerToString];

在回调中(id 值是pointerToString)。我使用 pointerToString 初始化 NSData,同时使用 base64 编码对其进行解码。
^(id value)
{
    if ( [[value class] isSubclassOfClass:[NSString class]] ) 
    {
    NSData *data = [NSData dataFromBase64String:value];
    [data writeToFile:file.path atomically:YES];
}
}

当 NSData 调用之后或调用期间内存分配达到大约 130MB 时,iPad 1 设备内存不足并被 iOS 杀死。

我已经确定,为了以这种方式处理 40+MB 的 NSString,我需要大约 180+MB 的 RAM(这是 iPad 2 和 3 上的最大内存分配,由于更多的 RAM,该过程可以正常工作)

任何想法/提示?

谢谢

最佳答案

编辑 :

在处理这种大小的文件时,您可能不想一次将整个数兆字节的文件加载到内存中,无论是巨大的输入文件还是几乎一样大的输出文件。您应该以流式方式解析它,解码 foundCharacters 中的数据。随着你的进行,没有在内存中保留任何重要的部分。

但是,传统技术可能会在过程的三个阶段保留整个 XML 文件内存:

  • 当您从服务器下载 XML 文件时;
  • 当 XML 解析器解析该文件时;和
  • 当您对文件进行 Base64 解码时。

  • 诀窍是采用流技术,对单个大型 XML 文件的小 block 同时执行这三个过程。最重要的是,当您下载整个 50mb 文件时,抓取几个 kb,解析 XML,如果您正在解析 Base64 编码字段,则对这几个 kb 执行 Base64 解码,然后继续下一个数据 block 。

    有关此示例(至少是流式 XML 下载和解析,不包括 Base64 解码),请参阅 Apple 的 XMLPerformance sample project .您将看到它将演示两个 XML 解析器,NSXMLParser我们都熟悉的,以及不太熟悉的 LibXML解析器。 NSXMLParser 的问题就是说,留给它自己的设备,即使您使用 initWithContentsOfURL,它也会在开始解析之前将整个 XML 文件加载到内存中。 .

    在我之前的回答中,我错误地声称使用 initWithContentsOfURL , NSXMLParser将在下载时以漂亮的小数据包解析 URL 的内容。 foundCharacters NSXMLParserDelegate的方法协议(protocol)看起来很像 NSURLConnectionDelegate方法,didReceiveData ,我确信 NSXMLParser将像 NSURLConnection 一样处理流确实,即在下载过程中返回信息。可悲的是,它没有。

    通过使用 LibXML但是,就像 Apple XMLPerformance 示例项目一样,您实际上可以使用 NSURLConnection流的能力,从而动态解析 XML。

    我创造了一点test project ,但我可能建议您详细了解 Apple 的 XMLPerformance 示例项目。但在我的实验中,一个 56mb 的 XML 文件在通过 NSXMLParser 解析和转换时消耗了超过 100mb 的空间。但在使用 LibXML2 时只消耗了 2mb .

    在您的评论中,您描述了将 Base64 编码数据下载到文件然后对其进行解码的愿望。这种方法似乎效率低得多,但肯定可行。顺便说一下,在初次下载时,您遇到了相同的内存问题(我在上面解决了这个问题)。我敦促您确保您对 Base64 编码数据的初始下载不会像大多数例程那样轻松地将其加载到 RAM 中。你想,假设你正在使用 NSURLConnection ,将数据写入NSOutputStream当您收到 didReceiveData 中的数据时,不要将其保存在 RAM 中。

    didReceiveResponse在 Apple 的 AdvancedGetController.m 中 AdvancedURLConnections example有关如何在接收文件时写入文件的示例,而不是将其添加到 NSMutableData 的典型模式(因为大多数这些例程只是假设您正在处理一个合理大小的文件)。 (忽略 AdvancedURLConnections 示例中有关身份验证等的所有内容,但重点了解它是如何写入 NSOutputStream 的。)此技术将解决此答案顶部列出的三个问题中的第一个,但不是后两者。为此,您必须考虑使用 LibXML2如 Apple 的 XMLPerformance 示例项目或其他类似技术中所示。

    关于ios - 解码一个巨大的 NSString,内存不足,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13691787/

    相关文章:

    ios - 是否可以仅让应用程序的某些部分支持 iPad View ?

    java - 将 GWT 部署到 JBoss 时出现 web.xml 问题

    c++ - 指针或堆对象的二维数组

    c# - 获取进程的 CPU 和内存使用情况的正确性能计数器是什么?

    ios - 如何在顶部状态栏中显示加载指示器

    ios - 防止 NSURLProtocol 实现读取 URL

    ios - 为什么不可靠地调用 UIApplicationDelegate 方法 `application(_:configurationForConnecting:options:)`

    c# - 如何用前缀替换 xmlns 命名空间属性?

    xml - 书籍、杂志和其他形式媒体的架构

    objective-c - ARC引用计数解除分配和释放