1

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

XML结构:

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

经过大量研究,包括XMLSchema 示例项目Ray Wenderlich XML 教程和一些 stackoverflow 条目,我们还没有找到解决方案。

我们首先下载 XML-File,然后开始解析并将数据插入到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);
        }

    }];
  }

}

使用这种方法,内存使用量会激增,导致崩溃。XML 文件似乎在Core Data. 所以问题是:

是否可以处理部分 XML 文件(一次 fe 30 个条目),而不是保存到CoreData然后继续解析?

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

4

3 回答 3

4

您想使用基于流的解析器,因此您不需要同时将整个 XML 加载到内存中。也许这个或来自github的东西。

您还应该批量保存操作。不要保存每个单独的对象,保存大约 100 个对象的组。如果这是在一个紧密的循环中,你应该有一个自动释放池。

于 2013-08-21T15:51:39.000 回答
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 个 bundle

解析器

// 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;
}
于 2013-08-23T12:56:03.007 回答
0

基于 DOM 的解析器非常方便(TBXML、TouchXML、KissXML、TinyXML、GDataXML、RaptureXML 等),尤其是那些支持 XPATH 的解析器。但是,随着 DOM 的创建,内存成为一个问题。

我正在逐步使用相同的内存限制,所以我开始查看Libxml2 XmlTextReader的包装器,到目前为止我只找到了一个IGXMLReader

IGXMLReader 解析 XML 文档的方式类似于光标移动的方式。Reader 将获得一个 XML 文档,并为每次调用 nextObject 返回一个节点(一个 IGXMLReader 对象)。

例子,

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

于 2016-03-13T00:45:52.437 回答