NSXMLParser Эффективность распределения памяти для iPhone - PullRequest
6 голосов
/ 22 января 2010

Я недавно играл с кодом для приложения для iPhone для анализа XML. Придерживаясь Какао, я решил пойти с классом NSXMLParser. Приложение будет отвечать за анализ 10 000+ «компьютеров», которые содержат 6 других строк информации. Для моего теста я убедился, что размер XML составляет около 900 КБ-1 МБ.

Моя модель данных - хранить каждый компьютер в NSDictionary, хэшированном уникальным идентификатором. Каждый компьютер также представлен NSDictionary с информацией. В итоге я получаю NSDictionary, содержащий 10 тыс. Других NSDictionaries.

Проблема, с которой я сталкиваюсь, не связана с утечкой памяти или эффективным хранением структуры данных. Когда мой синтаксический анализатор закончил, общее количество выделенных объектов только увеличивается примерно на 1 МБ. Проблема в том, что во время работы NSXMLParser мое выделение объектов увеличивается на целых 13 МБ. Я мог понять 2 (один для объекта, который я создаю, а другой для необработанных NSData) плюс немного места для работы, но 13 кажется немного высоким. Я не могу представить, что NSXMLParser настолько неэффективен. Мысли? * * 1005

Код ...

Код для начала разбора ...

NSXMLParser *parser = [[NSXMLParser alloc] initWithData: data];
[parser setDelegate:dictParser];
[parser parse];
output = [[dictParser returnDictionary] retain];        
[parser release];
[dictParser release];

И код делегата парсера ...

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {

    if(mutableString)
    {
        [mutableString release];
        mutableString = nil;

    }

    mutableString = [[NSMutableString alloc] init];     

}

-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { 
    if(self.mutableString)
    {

        [self.mutableString appendString:string];

    }
}

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {

    if([elementName isEqualToString:@"size"]){
        //The initial key, tells me how many computers
        returnDictionary = [[NSMutableDictionary alloc] initWithCapacity:[mutableString intValue]];
}

    if([elementName isEqualToString:hashBy]){
    //The unique identifier
        if(mutableDictionary){
            [mutableDictionary release];
            mutableDictionary = nil;
    }       

        mutableDictionary = [[NSMutableDictionary alloc] initWithCapacity:6];

        [returnDictionary setObject:[NSDictionary dictionaryWithDictionary:mutableDictionary] forKey:[NSMutableString stringWithString:mutableString]];
}

    if([fields containsObject:elementName]){
        //Any of the elements from a single computer that I am looking for
        [mutableDictionary setObject:mutableString forKey:elementName];
}
}

Все инициализировано и выпущено правильно. Опять же, я не получаю ошибки или утечки. Просто неэффективно.

Спасибо за любые мысли!

Ответы [ 7 ]

6 голосов
/ 23 января 2010

NSXMLParser - это проблема с памятью:

  1. это не настоящий потоковый парсер: initWithURL: загрузит полную версию XML перед обработкой. Для памяти использовать это плохо, как это должно выделить память для полного XML которые не могут быть восстановлены до конец разбора. Для производительности это тоже плохо, так как нельзя чередовать IO интенсивная часть загрузки и процессор интенсивно обрабатывает часть.
  2. это не освободит память. Похоже на то что строки / словари созданы во время разбора держится вокруг до конца разбора. Я пробовал улучшить его с помощью творческого использования NSAutoreleasePool но без каких-либо успех.

Альтернативами являются libxml и AQXMLParser , который является NSXMLParser-совместимой оболочкой для libxml, или ObjectiveXML .

Подробнее см. в моем блоге .

3 голосов
/ 22 января 2010

Не могу сказать ничего конкретного о вашем коде, но взгляните на пример XMLPerformance от Apple - он сравнивает производительность NSXMLParser и libxml - результаты определенно в пользу последнего. В одном из моих проектов переход с NSXMLParser на libxml дал значительный прирост производительности, поэтому я бы предложил использовать его.

0 голосов
/ 14 июня 2010

Ранее я использовал AQXMLParser , и он определенно намного эффективнее, чем NSXMLParser.

0 голосов
/ 25 мая 2010

Если вы ищете замену NSXMLParser, которая может обрабатывать потоковые большие XML-документы через http, вас может заинтересовать мой Expat Objective C Wrapper .

0 голосов
/ 25 января 2010

Только что переключился на libxml .

Немного головной боли, но ссылка, которую Владимир разместил, очень помогла.

Теперь размер файла 900k - 1mb составляет всего 2-3мб. Кроме того, поскольку это потоковый парсер, он выполняется почти сразу после возврата NSURLRequest.

Окончательный ответ - libxml.

Спасибо за вашу помощь, ребята!

0 голосов
/ 23 января 2010

Если вы хотите узнать, куда уходит ваша память, запустите код в разделе «Инструменты» с помощью шаблона ObjectAlloc и отсортируйте список классов по общему размеру. Как только общее использование памяти станет огромным, вы увидите, что один или несколько классов являются крупнейшими пользователями памяти.

Затем перейдите к одному из этих классов и изучите его экземпляры, чтобы увидеть, что их создало.

Тогда вы узнаете из доказательств, в чем заключается ваша проблема.

0 голосов
/ 22 января 2010

Я использовал NSXMLParser для анализа XML-файлов с 500 записями на 700K или около того. Я обнаружил, что это был верхний предел ограничения памяти iPhone 3G. Объем памяти увеличился до размера, превышающего размер XML-файла, и иногда достигал 15 МБ. Проблема была в том, что я хранил записи в массиве, так что оба были в памяти одновременно. Когда разбор законченной памяти снова уменьшился, но если он достигнет 15 или 20 МБ, приложение зависнет. Предполагается, что libxml намного эффективнее использует память.

Вы также можете попытаться сохранить созданные объекты с Базовыми данными, а не в массиве. Core Data больше заботится о памяти, освобождая объекты, когда они не нужны.

С моим приложением я уменьшил накладные расходы памяти, оптимизировав другие части, чтобы общий объем используемой памяти никогда не достигал верхнего предела.

...