如何提高iOS代码的XML解析性能?

Tit*_*eul 0 xml objective-c xml-parsing ios

这可能已被问过很多,但我还是迷路了.我需要解析从Google Reader的API检索的XML文件.基本上,它包含如下对象:

<object>
    <string name="id">feed/http://developer.apple.com/news/rss/news.rss</string>
    <string name="title">Apple Developer News</string>
    <list name="categories">
        <object>
            <string name="id">user/17999068807557229152/label/Apple</string>
            <string name="label">Apple</string>
        </object>
    </list>
    <string name="sortid">DB67AFC7</string>
    <number name="firstitemmsec">1317836072018</number>
    <string name="htmlUrl">http://developer.apple.com/news/</string>
</object>
Run Code Online (Sandbox Code Playgroud)

我尝试过使用NSXMLParser并且它可以工作,但它确实很慢.也许我的代码不是最有效的,但仍然需要10秒以上才能解析并将对象保存到Core Data中.我还看了几个其他库,但对于这么小的XML文件,它们的使用看起来有点复杂和沉重.

你觉得我应该怎么用?

谢谢.

编辑

这里是解析器代码:

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

    if([elementName isEqualToString:@"list"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"subscriptions"]){
        subscriptionListFound = YES;
    }

    if(subscriptionListFound){
        if([elementName isEqualToString:@"list"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"categories"]){
            categoryFound = YES;
            currentCategoryId = [[[NSMutableString alloc] init] autorelease];
            currentCategoryLabel = [[[NSMutableString alloc] init] autorelease];
        }
        if([elementName isEqualToString:@"object"] && !subscriptionFound && !categoryFound){
            subscriptionFound = YES;
            currentSubscriptionTitle = [[[NSMutableString alloc] init] autorelease];
            currentSubscriptionId = [[[NSMutableString alloc] init] autorelease];
            currentSubscriptionHtmlURL = [[[NSMutableString alloc] init] autorelease];
        }
        if([elementName isEqualToString:@"string"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"id"]){
            if(categoryFound){
                categoryIdFound = YES; 
            }
            else{
                subscriptionIdFound = YES;
            }
        }
        if([elementName isEqualToString:@"string"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"title"]){
            subscriptionTitleFound = YES;
        }
        if([elementName isEqualToString:@"string"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"label"]){
            categoryLabelFound = YES;
        }
        if([elementName isEqualToString:@"string"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"htmlUrl"]){
            subscriptionHtmlURLFound = YES;
        }
    }
}

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

    if([elementName isEqualToString:@"list"] && !categoryFound){
        subscriptionListFound = NO;
    }

    if([elementName isEqualToString:@"list"] && categoryFound){
        categoryFound = NO;
    }

    if([elementName isEqualToString:@"object"] && !categoryFound && subscriptionFound){        
        [self saveSubscription];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"currentSubscriptionNotification" object:currentSubscriptionTitle];
        subscriptionFound = NO;
    }

    if([elementName isEqualToString:@"string"]){
        if(subscriptionIdFound == YES) {
            [currentSubscriptionId appendString:self.currentParsedCharacterData];
            subscriptionIdFound = NO;
        }
        if(subscriptionTitleFound == YES) {
            [currentSubscriptionTitle appendString:self.currentParsedCharacterData];
            subscriptionTitleFound = NO;
        }
        if(subscriptionHtmlURLFound == YES) {
            [currentSubscriptionHtmlURL appendString:self.currentParsedCharacterData];
            subscriptionHtmlURLFound = NO;
        }
        if(categoryIdFound == YES) {
            [currentCategoryId appendString:self.currentParsedCharacterData];
            categoryIdFound = NO;
        }
        if(categoryLabelFound == YES) {
            [currentCategoryLabel appendString:self.currentParsedCharacterData];
            categoryLabelFound = NO;
        }
    }

    [self.currentParsedCharacterData setString:@""];
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    [self.currentParsedCharacterData appendString:string];
}
Run Code Online (Sandbox Code Playgroud)

这里是通过CoreData保存的代码:

- (void) saveSubscription {

    NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
    [fetchRequest setEntity:
     [NSEntityDescription entityForName:@"Group" inManagedObjectContext:context]];
    [fetchRequest setPredicate: [NSPredicate predicateWithFormat: @"(id == %@)",self.currentCategoryId]];
    [fetchRequest setSortDescriptors: [NSArray arrayWithObject:
                                        [[[NSSortDescriptor alloc] initWithKey: @"id"
                                        ascending:YES] autorelease]]];

    NSError *error2 = nil;
    NSArray *foundGroups = [context executeFetchRequest:fetchRequest error:&error2];

    if ([foundGroups count] > 0) {
        self.currentGroupObject = [foundGroups objectAtIndex:0];
    }
    else {
        self.currentGroupObject = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:context];
        [self.currentGroupObject setId:self.currentCategoryId];
        [self.currentGroupObject setLabel:self.currentCategoryLabel];
    }

    fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
    [fetchRequest setEntity:
     [NSEntityDescription entityForName:@"Subscription" inManagedObjectContext:context]];
    [fetchRequest setPredicate: [NSPredicate predicateWithFormat: @"(id == %@)", self.currentSubscriptionId]];
    [fetchRequest setSortDescriptors: [NSArray arrayWithObject:
                                       [[[NSSortDescriptor alloc] initWithKey: @"id"
                                                                    ascending:YES] autorelease]]];

    error2 = nil;
    NSArray *foundSubscriptions = [context executeFetchRequest:fetchRequest error:&error2];

    if ([foundSubscriptions count] > 0) {
        self.currentSubscriptionObject = [foundSubscriptions objectAtIndex:0];
    }
    else {
        self.currentSubscriptionObject = [NSEntityDescription insertNewObjectForEntityForName:@"Subscription" inManagedObjectContext:context];
        [self.currentSubscriptionObject setId:self.currentSubscriptionId];
        [self.currentSubscriptionObject setTitle:self.currentSubscriptionTitle];
        [self.currentSubscriptionObject setHtmlURL:self.currentSubscriptionHtmlURL];
        NSString *faviconURL = [self favIconUrlStringFromURL:self.currentSubscriptionHtmlURL];
        NSString *faviconPath = [self saveFavicon:self.currentSubscriptionTitle url:faviconURL];
        [self.currentSubscriptionObject setFaviconPath:faviconPath];
        [self.currentSubscriptionObject setGroup:self.currentGroupObject];
        [self.currentGroupObject addSubscriptionObject:self.currentSubscriptionObject];
    }

    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*ood 9

你的解析逻辑是非常低效的 - 你说一遍又一遍地做同样的测试

if (string and x) do this
if (string and y) do this
if (string and z) do this
Run Code Online (Sandbox Code Playgroud)

代替

if (string)
    if (x) do this
    if (y) do this
    if (z) do this
Run Code Online (Sandbox Code Playgroud)

所有那些不必要的字符串比较可能是你解析速度太慢的原因.所有对象查找都是一样的.如果您需要多次值,请获取一次,然后将其存储在变量中.

Objective C方法调用相对较慢,无法通过编译器进行优化,因此如果值没有更改,则应调用该方法一次然后存储它.

例如,这个:

if([elementName isEqualToString:@"string"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"id"]){
    if(categoryFound){
        categoryIdFound = YES; 
    }
    else{
        subscriptionIdFound = YES;
    }
}
if([elementName isEqualToString:@"string"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"title"]){
    subscriptionTitleFound = YES;
}
if([elementName isEqualToString:@"string"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"label"]){
    categoryLabelFound = YES;
}
if([elementName isEqualToString:@"string"] && [[attributeDict objectForKey:@"name"] isEqualToString:@"htmlUrl"]){
    subscriptionHtmlURLFound = YES;
}
Run Code Online (Sandbox Code Playgroud)

可以改写为:

NSString *name = [attributeDict objectForKey:@"name"];
if([elementName isEqualToString:@"string"])
{
    if ([name isEqualToString:@"id"])
    {
        if(categoryFound){
            categoryIdFound = YES; 
        }
        else{
            subscriptionIdFound = YES;
        }
    }
    else if ([name isEqualToString:@"title"])
    {
        subscriptionTitleFound = YES;
    }
    else if ([name isEqualToString:@"label"])
    {
        categoryLabelFound = YES;
    }
    else if ([name isEqualToString:@"htmlUrl"])
    {
        subscriptionHtmlURLFound = YES;
    }
}
Run Code Online (Sandbox Code Playgroud)

哪种方式更有效率.

  • 我不知道这种方式更有效率.谢谢你的解释. (2认同)