目标C:如何将巨大的XML数据导入Core Data

时间:2013-08-21 15:45:18

标签: iphone objective-c xml core-data

我们想要将一个巨大的XML文件(13MB)导入Core Data。目前,XML-File包含大约64000个条目,但这个数字将来会增加。

XML-结构:

<entry name='...' doctype='' last-modified='...' [some more attributes]  />

经过大量研究,其中包括XMLSchema Sample ProjectRay 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,之后继续解析?

或者更常见的问题:如何优化内存使用?

3 个答案:

答案 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的方法不同。