iPhone的NSXMLParser内存分配效率问题

6
我最近在编写一个iPhone应用程序的代码,用于解析XML。为了保持与Cocoa框架的一致性,我选择使用NSXMLParser类。这个应用程序将负责解析10,000多台“计算机”,每台计算机都包含6个字符串类型的信息。为了测试,我验证了XML文件大小约为900k-1MB。
我的数据模型是将每台计算机存储在NSDictionary中,以唯一标识符进行哈希。每台计算机也由一个NSDictionary表示其信息。因此,在一天结束时,我得到了一个包含10k个其他NSDictionary的NSDictionary。
我遇到的问题不是内存泄漏或有效的数据结构存储问题。当我的解析器完成后,总分配对象的数量仅会增加约1MB。问题在于当NSXMLParser运行时,我的对象分配会跳跃性地增加高达13MB。我可以理解有2个(一个是我正在创建的对象,另一个是原始NSData),再加上一些工作空间,但13MB似乎有点太高了。我无法想象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];
}
}

所有初始化和释放都正确。再次强调,我没有收到错误或泄漏的提示。只是效率低下。

感谢您的任何想法!

7个回答

6

NSXMLParser是一个内存占用大户:

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

替代方案是libxml和AQXMLParser,它是围绕libxml的NSXMLParser兼容包装器,或者ObjectiveXML

有关更多详细信息,请参见我的博客文章


啊,这就解释了为什么这篇文章似乎起作用 - 切换到 initWithData 似乎可以解决问题:http://blog.filipekberg.se/2010/11/30/nsxmlparser-has-memory-leaks-in-ios-4/ - PostCodeism

3

无法对您的代码做出具体评论,但可以看看苹果公司的XMLPerformance示例 - 它比较了NSXMLParser和libxml的性能 - 结果明显有利于后者。在我的一个项目中,从NSXMLParser切换到libxml极大地提高了性能,因此建议使用它。


libxml 能处理 SSL 解析吗?仅仅做了一个快速搜索,我没有找到太多相关的信息。如果它不能处理 SSL,那对我来说就是个致命伤。 - Staros

0
如果你正在寻找一种替代NSXMLParser的解析器,可以处理大型XML文档在HTTP上的流式传输,那么你可能会对我的Expat Objective C Wrapper感兴趣。

0

我以前用过AQXMLParser,它绝对比NSXMLParser更节省内存。


0
我用NSXMLParser解析了约500条记录、大约700K的XML文件。我发现这已经接近iPhone 3G内存限制的上限了。内存扩展到比XML文件的大小多得多,有时达到15MB。问题在于我把记录存储在一个数组中,所以两者同时存在于内存中。当解析完成后,内存会再次下降,但如果它达到了15或20MB,应用程序就会崩溃。据说libxml具有更高的内存效率。
您还可以尝试使用Core Data而不是数组来存储创建的对象。Core Data通过在不需要时释放对象来更好地管理内存。
对于我的应用程序,我通过优化其他部分来减少内存开销,以使总使用内存从未达到上限。

0

如果你想知道内存去了哪里,可以使用ObjectAlloc模板在Instruments下运行代码,并按总大小对类列表进行排序。一旦整体内存使用量变得巨大,你会看到一个或几个类占用了最多的内存。

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

这样,你就可以从证据中知道问题所在了。


0

刚刚转换到libxml

有点头疼,但Vladimir发布的链接帮了大忙。

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

最终答案 - libxml。

感谢大家的帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接