Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 如何提高iOS代码的XML解析性能?_Objective C_Ios_Xml_Xml Parsing - Fatal编程技术网

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

Objective c 如何提高iOS代码的XML解析性能?,objective-c,ios,xml,xml-parsing,Objective C,Ios,Xml,Xml Parsing,这可能被问了很多,但我还是迷路了。我需要解析从GoogleReader的API检索到的XML文件。基本上,它包含以下对象: <object> <string name="id">feed/http://developer.apple.com/news/rss/news.rss</string> <string name="title">Apple Developer News</string> <list

这可能被问了很多,但我还是迷路了。我需要解析从GoogleReader的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>
以下是通过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]);
    }
}

我建议您使用GDataXML。它使用起来非常简单,速度也非常快。欲了解更多信息,请访问


在这个堆栈溢出主题中,我已经回答了关于如何使用GDataXML读取属性的类似问题:。

我认为,在iOS上解析XML的最佳库是。它允许您使用解析XML,并具有高级元素解析选项。您还可以使用此函数解析XHTML文档

解析非常简单:

NSData *xmlData = read your xml file
CXMLDocument *doc = [[CXMLDocument alloc] initWithData:xmlData options:0 error:nil]
NSArray *objects = [doc nodesForXPath:@"//object" error:nil];

for (CXMLElement *object in objects) {
    NSArray *children = [object children];
    for(CXMLElement *child in children) {
        if([[child name] isEqualToString:@"string"]) {
            // you are parsing <string> element.
            // you can obtain element attribute by:
            NSString *name = [[child attributeForName:@"name"] stringValue];
            // you can obtain string between <></> tags via:
            NSString *value = [child stringValue];
        } else if([[child name] isEqualToString:@"list"]) {
            // you are parsing <list> element.
        } else if ... 
    }
}
NSData*xmlData=读取xml文件
CXMLDocument*doc=[[CXMLDocument alloc]initWithData:xmlData选项:0错误:无]
NSArray*objects=[doc nodesForXPath:@//object”错误:nil];
用于(对象中的CXMLElement*对象){
NSArray*children=[对象子对象];
for(CXMLElement*子对象中的子对象){
if([[child name]IsequalString:@“string”]){
//您正在分析元素。
//您可以通过以下方式获得元素属性:
NSString*name=[[child attributeForName:@“name”]stringValue];
//您可以通过以下方式获得标签之间的字符串:
NSString*值=[子字符串值];
}else if([[child name]IsequalString:@“list”]){
//您正在分析元素。
}否则如果。。。
}
}

您的解析逻辑效率很低-您一遍又一遍地执行相同的测试

if (string and x) do this
if (string and y) do this
if (string and z) do this
而不是

if (string)
    if (x) do this
    if (y) do this
    if (z) do this
所有这些不必要的字符串比较可能就是解析如此缓慢的原因。所有的对象查找也是如此。如果您多次需要一个值,请获取一次,然后将其存储在变量中

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;
}
可以改写为:

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;
    }
}

这是更有效的方式。

在开发了一些与您的需求类似的应用程序之后,我衷心推荐

我通常的XML解析设置大致如下:

  • 使用GCD og NSOperationsQueue创建一个单独的队列
  • 使用HTTPMessage和AQGZipInputStream设置输入流
示例代码:

HTTPMessage *message = [HTTPMessage requestMessageWithMethod:@"GET" url:url version:HTTPVersion1_1];
[message setUseGzipEncoding:YES];       
AQGzipInputStream *inputstream = [[AQGzipInputStream alloc] initWithCompressedStream:         [message inputStream]];
  • 将流交给单独的解析器委托,该委托创建单独的NSManagedObjectContext,并在保存时将更改合并到主NSManagedObjectContext中(NSManagedObject不是线程安全的!)
初始化上下文和添加合并通知的示例代码:

-(void)parserDidStartDocument:(AQXMLParser *)parser
{
  self.ctx=[[NSManagedObjectContext alloc] init];
  [self.ctx setMergePolicy: NSMergeByPropertyObjectTrumpMergePolicy];
  [self.ctx setPersistentStoreCoordinator: [Database db].persistentStoreCoordinator];
  NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
  [dnc addObserver:self selector:@selector(mergeContextChanges:) name:NSManagedObjectContextDidSaveNotification object:self.ctx];  
  parsedElements = 0;
}

- (void)mergeContextChanges:(NSNotification *)notification{
  SEL selector = @selector(mergeHelper:);
  [self performSelectorOnMainThread:selector withObject:notification waitUntilDone:YES];
}

- (void)mergeHelper:(NSNotification*)saveNotification
{
// Fault in all updated objects
NSArray* updates = [[saveNotification.userInfo objectForKey:@"updated"] allObjects];
for (NSInteger i = [updates count]-1; i >= 0; i--)
{
    [[[Database db].managedObjectContext objectWithID:[[updates objectAtIndex:i] objectID]] willAccessValueForKey:nil];
}

// Merge
[[Database db].managedObjectContext    mergeChangesFromContextDidSaveNotification:saveNotification];
}
在我看来,对于大型数据集,选择正确的解析器更为关键。如果您的数据集是可管理的,那么您可以从良好的实现中获得很多好处。使用任何基于libxml的解析器,并在接收数据块时对其进行解析,将使您在下载数据后对其进行解析,从而显著提高性能


根据您的数据源,libz可能抛出Z_BUF_错误(至少在模拟器中)。我已经在AQToolkit上的pull请求中提出了一个解决方案,但我非常肯定会有更好的解决方案

如果您愿意添加代码,我们可能会帮助您改进。我重新命名了这个问题,因为“最佳库”问题基本上只是意见,您在这个场景中寻找特定的性能增强,无论它是否涉及新库。谢谢您的建议,但您所说的对象查找到底是什么意思?你说的是代码的哪一部分?[attributeDict objectForKey:@“name”]我不知道这种方法更有效。谢谢你的解释。我同意Flex_对GDataXML的痴迷,但我也想添加一个链接,指向这篇关于在ios上选择xml解析器的非常有用的文章
-(void)parserDidStartDocument:(AQXMLParser *)parser
{
  self.ctx=[[NSManagedObjectContext alloc] init];
  [self.ctx setMergePolicy: NSMergeByPropertyObjectTrumpMergePolicy];
  [self.ctx setPersistentStoreCoordinator: [Database db].persistentStoreCoordinator];
  NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
  [dnc addObserver:self selector:@selector(mergeContextChanges:) name:NSManagedObjectContextDidSaveNotification object:self.ctx];  
  parsedElements = 0;
}

- (void)mergeContextChanges:(NSNotification *)notification{
  SEL selector = @selector(mergeHelper:);
  [self performSelectorOnMainThread:selector withObject:notification waitUntilDone:YES];
}

- (void)mergeHelper:(NSNotification*)saveNotification
{
// Fault in all updated objects
NSArray* updates = [[saveNotification.userInfo objectForKey:@"updated"] allObjects];
for (NSInteger i = [updates count]-1; i >= 0; i--)
{
    [[[Database db].managedObjectContext objectWithID:[[updates objectAtIndex:i] objectID]] willAccessValueForKey:nil];
}

// Merge
[[Database db].managedObjectContext    mergeChangesFromContextDidSaveNotification:saveNotification];
}