Swift程序读取一个大的XML文件(几GB),一直占用内存,直到整个系统崩溃。不好的。在深入研究并删除所有有用的代码之后,下面的代码仍然存在。它只定义了一个实现了一个协议方法的NSXMLParserDelegate
。现在,当对一个17MB的相对较小的XML文件运行时,总分配量将达到47MB,脏内存占77MB。现在这让我觉得很奇怪,因为我的代码没有引用它传递的任何数据。
这是NSXMLParser的错误、我的误解还是我的代码的错误?
import Foundation
var input = NSURL(fileURLWithPath: Process.arguments[1])!
class MyDelegate: NSObject, NSXMLParserDelegate {
func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: NSDictionary) {
}
}
var parser = NSXMLParser(contentsOfURL: input)!
var delegate = MyDelegate()
parser.delegate = delegate
parser.parse()
文档
在解析XML时,内存管理成为一个高度关注的问题。处理XML通常需要创建许多对象;不应允许这些对象在内存中累积超过其有效范围。处理这些生成对象的一种技术是,委托在每个实现的委托方法的开头创建一个本地自动释放池,并在返回之前释放自动释放池。NSXMLParser为它创建并发送给委托的每个对象管理内存。
(source)
更新
当直接使用libxml2的sax解析器时,内存使用量在几秒钟后保持稳定,大约为100mb。为什么NSXMLParser(主要只是一个包装器)使用这么多内存?
更新2
NSXMLParser不应在委托处理数据后保留这些数据。NSXMLParser分配的大多数结构的ref计数为1(请参见屏幕截图),因此仍保持分配状态。手动释放内存是有帮助的,但这与文档中的内存语句相矛盾,而且感觉不对。
最佳答案
根据实验,我认为NSXMLParser
确实采用了全局autoreleasepool
。因此autoreleasepool
只管理整个解析活动,而不是对委托的单个回调。因此,在分析文件时内存压力会增大,只有在分析完完整的文件后才会释放。
伪代码:
Call function parse
@autoreleasepool {
Call delegate function with NSDictionary {
Bridge NSDictionary to Swift dictionary
Call my Swift delegate with Swift dictionary {
// my code
}
}
}
}
因此,
NSDictionary
和桥接的Swift字典都留在堆中,直到解析函数完成。为了减少内存压力,不要使用Swift委托,因此堆中没有Swift字典。或者,远离NSXMLParser
并实现libxml的sax解析器并包含更多的autoreleasepool
s。关于objective-c - NSXMLParser不释放分配?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26514909/