2

我正在寻找有关如何改进使用 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,该过程有效)

任何想法/提示?

谢谢

4

2 回答 2

2

编辑

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

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

  1. 当您从服务器下载 XML 文件时;

  2. 当 XML 解析器解析该文件时;和

  3. 正如您对文件进行 Base64 解码一样。

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

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

在我之前的回答中,我错误地声称通过使用initWithContentsOfURLNSXMLParser会在下载 URL 的内容时将其解析为漂亮的小数据包。协议的foundCharacters方法NSXMLParserDelegate似乎与NSURLConnectionDelegate方法非常相似didReceiveData,我确信它NSXMLParser会像处理流一样处理流NSURLConnection,即在下载正在进行时返回信息。可悲的是,它没有。

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

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


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

请参阅didReceiveResponseApple 的AdvancedURLConnections 示例的 AdvancedGetController.m中的示例,了解如何在收到文件时写入文件,而不是典型的将文件添加NSMutableData到)。(忽略 AdvancedURLConnections 示例中有关身份验证等的所有内容,但重点是了解它是如何写入的NSOutputStream。)此技术将解决此答案顶部列出的三个问题中的第一个,但不是后两者。为此,您必须考虑使用LibXML2Apple 的 XMLPerformance 示例项目中的说明或其他类似技术。

于 2012-12-04T04:44:55.367 回答
0

方法

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string

可能不会一次接收所有数据。医生在说

"由解析器对象发送以向其委托提供代表当前元素的全部或部分字符的字符串。 "

所以它被多次调用。看起来您正在尝试一次编写整个字符串(如果我错了,请见谅)。因此,您可以通过执行以下操作将接收到的数据附加到文件中:

您可以使用以下组合

-writeData: 

-seekToEndOfFile 

NSFileHandle 类中用于将 NSData 写入文件末尾的方法。

但是在接收部分数据时要小心你的 base64 编码!

于 2012-12-03T21:33:00.760 回答