6

我最近一直在使用用于解析 XML 的 iPhone 应用程序的代码。坚持使用 Cocoa,我决定使用 NSXMLParser 类。该应用程序将负责解析 10,000 多台“计算机”,所有这些都包含 6 个其他信息字符串。对于我的测试,我已验证 XML 的大小约为 900k-1MB。

我的数据模型是将每台计算机保存在由唯一标识符散列的 NSDictionary 中。每台计算机也由一个带有信息的 NSDictionary 表示。所以在一天结束的时候,我最终得到了一个包含 10k 个其他 NSDictionaries 的 NSDictionary。

我遇到的问题不是内存泄漏或高效的数据结构存储。当我的解析器完成后,分配对象的总量只增加了大约 1MB。问题是当 NSXMLParser 运行时,我的对象分配增加了 13MB。我可以理解 2(一个用于我正在创建的对象,一个用于原始 NSData)加上一点工作空间,但 13 似乎有点高。我无法想象 NSXMLParser 效率如此之低。想法?

代码...

开始解析的代码...

NSXMLParser *parser = [[NSXMLParser alloc] initWithData: data];
[parser setDelegate:dictParser];
[parser parse];
output = [[dictParser returnDictionary] retain];        
[parser release];
[dictParser release];

和解析器的委托代码......

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {

    if(mutableString)
    {
        [mutableString release];
        mutableString = nil;

    }

    mutableString = [[NSMutableString alloc] init];     

}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { 
    if(self.mutableString)
    {

        [self.mutableString appendString:string];

    }
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

    if([elementName isEqualToString:@"size"]){
        //The initial key, tells me how many computers
        returnDictionary = [[NSMutableDictionary alloc] initWithCapacity:[mutableString intValue]];
}

    if([elementName isEqualToString:hashBy]){
    //The unique identifier
        if(mutableDictionary){
            [mutableDictionary release];
            mutableDictionary = nil;
    }       

        mutableDictionary = [[NSMutableDictionary alloc] initWithCapacity:6];

        [returnDictionary setObject:[NSDictionary dictionaryWithDictionary:mutableDictionary] forKey:[NSMutableString stringWithString:mutableString]];
}

    if([fields containsObject:elementName]){
        //Any of the elements from a single computer that I am looking for
        [mutableDictionary setObject:mutableString forKey:elementName];
}
}

一切都已正确初始化和释放。同样,我没有收到错误或泄漏。只是效率低下。

感谢您的任何想法!

4

7 回答 7

6

NSXMLParser 是一个内存猪:

  1. 它不是一个真正的流解析器: initWithURL: 将在处理之前下载完整的 xml。对于内存使用,这是不好的,因为它必须为完整的 xml 分配内存,直到解析结束才能回收。就性能而言,它也很糟糕,因为您不能将下载的 IO 密集型部分和解析的 CPU 密集型部分交错。
  2. 它不会释放内存。解析期间创建的字符串/字典似乎一直保留到解析结束。我试图通过创造性地使用来改进它, NSAutoreleasePool但没有任何成功。

替代方案是 libxml 和AQXMLParser,它们是与 NSXMLParser 兼容的 libxml 包装器,或ObjectiveXML

有关详细信息,请参阅我的博客文章。

于 2010-01-22T21:23:01.517 回答
3

无法具体说明您的代码,但请看一下 Apple 的XMLPerformance示例——它比较了 NSXMLParser 和 libxml 的性能——结果肯定有利于后者。在我的一个项目中,从 NSXMLParser 切换到 libxml 极大地提升了性能,所以我建议使用它。

于 2010-01-22T15:51:41.163 回答
0

我使用 NSXMLParser 来解析 XML 文件,其中包含大约 500 条记录,大小为 700K 左右。我发现这是 iPhone 3G 内存限制的上限。内存扩大到远远超过 XML 文件的大小,有时达到 15MB。问题是我将记录存储在一个数组中,所以两者同时在内存中。当解析完成的内存再次下降,但如果达到 15 或 20MB,应用程序就会崩溃。libxml 的内存效率应该更高。

您也可以尝试使用 Core Data 存储创建的对象,而不是存储在数组中。Core Data 通过在不需要对象时释放对象来更多地处理内存。

在我的应用程序中,我通过优化其他部分来减少内存开销,从而使使用的总内存永远不会达到上限。

于 2010-01-22T16:39:43.270 回答
0

如果您想知道内存的去向,请使用 ObjectAlloc 模板运行 Instruments 下的代码,然后按总大小对类列表进行排序。一旦整体内存使用量变得巨大,您就会看到一个类或几个类是最大的内存占用者。

然后,深入研究其中一个类并检查它的实例,看看是什么创建了它们。

然后你会从证据中知道你的问题出在哪里。

于 2010-01-22T21:37:06.570 回答
0

刚刚切换到libxml

有点头疼,但弗拉基米尔发布的链接是一个巨大的帮助。

现在 900k - 1mb 文件的膨胀只有 2-3mb 左右。另外,因为它是一个流解析器,它几乎在NSURLRequest返回后立即完成。

最终答案 - libxml。

感谢您的所有帮助!

于 2010-01-25T03:42:37.620 回答
0

如果您正在寻找可以通过 http 处理大型 XML 文档流的 NSXMLParser 的替代品,您可能会对我的Expat Objective C Wrapper感兴趣。

于 2010-05-24T21:05:46.293 回答
0

我以前用过AQXMLParser,它的内存效率肯定比 NSXMLParser 高得多。

于 2010-06-14T00:11:19.797 回答