读取一个大的XML文件(几GB),Swift程序一直在吃内存,直到整个系统崩溃。不好。在挖掘所有有用的代码后,下面的代码仍然存在。它仅定义了NSXMLParserDelegate
,其中实现了一种协议方法。现在,当针对17 MB的相对较小的XML文件运行时,总分配将达到47 MB,而脏内存占77 MB。现在这让我感到奇怪,因为我的代码没有引用它传递的任何数据。
这是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解析器时,内存使用率在几秒钟后保持稳定,使用率约为100 MB。为什么NSXMLParser(主要是一个包装器)使用这么多内存?
更新2
NSXMLParser在委托处理完数据后不应该保留数据。 NSXMLParser分配的大多数结构的引用计数为1(参见屏幕截图),因此保持分配状态。手动释放内存会有所帮助,但这与文档中的内存声明相矛盾,并且感觉不对。
答案 0 :(得分:0)
根据实验,我认为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。