我们想要将一个巨大的XML文件(13MB)导入Core Data。目前,XML-File包含大约64000个条目,但这个数字将来会增加。
XML-结构:
<entry name='...' doctype='' last-modified='...' [some more attributes] />
经过大量研究,其中包括XMLSchema Sample Project,Ray Wenderlich XML Tutorial和一些stackoverflow条目,我们还没有找到解决方案。
我们首先下载XML文件,然后开始解析并将数据插入CoreData
这是我们的实施:
- (void)importXMLFile:(NSString*)fileName {
NSInputStream* theStream = [[NSInputStream alloc] initWithFileAtPath:fileName];
_theParser = [[NSXMLParser alloc] initWithStream:theStream];
_theParser.delegate = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[_theParser parse];
});
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:@"entry"]) {
Importer* __weak weakSelf = self;
NSManagedObjectContext* theContext = self.importContext;
[theContext performBlock:^{
CustomObject* mo;
// Create ManagedObject
// Read values from parsed XML element
dispatch_async(dispatch_get_main_queue(), ^{
// Call a handler, just for information "added object"
});
NSError *error = nil;
if ([theContext hasChanges] && ![theContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
} else {
DLOGError(error);
}
}];
}
}
使用此方法,内存使用量会爆炸,从而导致崩溃。在Core Data
处理一个块之前,似乎完全解析了XML文件。所以问题是:
是否可以处理部分XML文件(例如,每次30个条目),而不是保存到CoreData
,之后继续解析?
或者更常见的问题:如何优化内存使用?
答案 0 :(得分:4)
您希望使用基于流的解析器,因此您无需同时将整个XML加载到内存中。也许this或来自github的东西。
您还应该批量保存操作。不要保存每个单独的对象,保存可能包含100个对象的组。如果这是一个紧凑的循环,你应该有一个自动释放池。
答案 1 :(得分:2)
在创建我们的ManagedObject
时,猜测我们的内存问题是在我们未发布的行中发生的。我们必须释放xmlChar
而不是
xmlChar *xmlString = xmlTextReaderGetAttribute(reader, (xmlChar*)"someAttribute");
NSString *someAttributeToString = [NSString stringWithUTF8String:(const char *)xmlString];
我们使用了
xmlChar * nameString = xmlTextReaderGetAttribute(reader, (xmlChar*)"someAttribute");
if (attributeString)
{
[elementDict setValue:[NSString stringWithUTF8String:(const char*)attributeString] forKey:@"someAttribute"];
xmlFree(nameString);
}
我们在解析100个元素后暂停解析器并等待,直到这些元素写入CoreData
。之后,我们解析下一个100包
分析器
// Start the data parse
- (void) parse {
_dictionaryQeue = [NSMutableArray new];
xmlTextReaderPtr reader = xmlReaderForMemory([data bytes], [data length], NULL, NULL,
(XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING));
if (!reader) {
NSLog(@"Failed to create xmlTextReader");
return;
}
while (xmlTextReaderRead(reader)) {
@autoreleasepool {
while (_isPaused) {
//[NSThread sleepForTimeInterval:0.1];
}
switch (xmlTextReaderNodeType(reader)) {
case XML_READER_TYPE_ELEMENT: {
NSMutableDictionary* elementDict = [NSMutableDictionary new];
//Create Object
xmlChar * nameString = xmlTextReaderGetAttribute(reader, (xmlChar*)"name");
if (nameString)
{
[elementDict setValue:[NSString stringWithUTF8String:(const char*)nameString] forKey:@"name"];
xmlFree(nameString);
}
//...
if (self.collectDictionaries) {
[_dictionaryQeue addObject:elementDict];
NSArray* dictArray = [NSArray arrayWithArray:_dictionaryQeue];
if ([dictArray count] == self.maxCollectedDictionaries) {
dispatch_async(dispatch_get_main_queue(), ^{
if (saxDelegate && [(NSObject*)saxDelegate respondsToSelector:@selector(SAXDictionaryElements:finished:)]) {
[saxDelegate SAXDictionaryElements:dictArray finished:FALSE];
}
});
[_dictionaryQeue removeAllObjects];
_isPaused = TRUE;
}
}
elementDict = nil;
}
break;
case XML_READER_TYPE_END_ELEMENT: {
DLOGcomment(@"XML_READER_TYPE_END_ELEMENT");
if (self.collectDictionaries) {
NSArray* dictArray = [NSArray arrayWithArray:_dictionaryQeue];
if ([dictArray count] > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if (saxDelegate && [(NSObject*)saxDelegate respondsToSelector:@selector(SAXDictionaryElements:finished:)]) {
[saxDelegate SAXDictionaryElements:dictArray finished:TRUE];
}
});
data = nil;
[_dictionaryQeue removeAllObjects];
_dictionaryQeue = nil;
}
}
}
break;
}
}
}
xmlTextReaderClose(reader);
xmlFreeTextReader(reader);
reader = NULL;
}
答案 2 :(得分:0)
基于DOM的解析器非常方便(TBXML,TouchXML,KissXML,TinyXML,GDataXML,RaptureXML等),特别是那些支持XPATH的解析器。但是,随着DOM的创建,内存成为一个问题。
我正在逐步调整相同的内存约束,所以我开始查看 Libxml2 XmlTextReader 的包装器,到目前为止我只发现了一个IGXMLReader
IGXMLReader解析类似于游标方式的XML文档 移动。 Reader被赋予一个XML文档,并返回一个节点(一个 IGXMLReader对象)每次调用nextObject。
实施例,
IGXMLReader* reader = [[IGXMLReader alloc] initWithXMLString:@"<x xmlns:edi='http://ecommerce.example.org/schema'>\
<edi:foo>hello</edi:foo>\
</x>"];
for (IGXMLReader* node in reader) {
NSLog(@"node name: %@", node.name);
}
这与NSXMLParser的方法不同。