【问题标题】:NSScanner - SLOW performance - (UITableView, NSXMLParser)NSScanner - 性能缓慢 - (UITableView, NSXMLParser)
【发布时间】:2012-08-02 12:48:48
【问题描述】:

我遇到了一个困扰我好几天的问题。

我正在使用 NSXMLParser 解析 RSS 提要,并将结果输入 UITableView。不幸的是,提要返回了一些我用以下方法解析出来的 HTML:

- (NSString *)flattenHTML:(NSString *)html {

NSScanner *theScanner;
NSString *text = nil;
theScanner = [NSScanner scannerWithString:html];
while ([theScanner isAtEnd] == NO) {
    [theScanner scanUpToString:@"<" intoString:NULL] ;
    [theScanner scanUpToString:@">" intoString:&text] ;
    html = [html stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%@>", text] withString:@""];
}
html = [html stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

return html;

}

我目前在 NSXMLParser 委托方法期间调用此方法:

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

这很好用,但是解析 HTML 并将其展平为文本并填充单元格需要将近一分钟或更长时间。在那无休止的一分钟内,我的 UITableView 完全是空的,只有一个旋转的微调器。这不好。这是在我发布这个原本可以很好工作的应用程序之前消除的最后一个“错误”。

它在 iOS 模拟器上运行得非常快,这不足为奇。

提前感谢您的任何建议。

【问题讨论】:

  • 您确定扫描仪是瓶颈吗?你测量过(使用仪器)吗?听起来好像实际的数据传输也可能是一个问题。
  • 您正在处理的数据有多大?任何低于兆字节的东西都应该移动得很快。比这更大,你会看到NSXMLParser 开始挣扎。
  • 对于完全留在记忆中的东西来说,一分钟慢得令人难以置信。添加NSLog 以了解那一分钟有多少时间用于通过网络接收数据,或者您重复执行某项操作的次数超过了您必须执行的次数。
  • @Till,数据传输不是瓶颈。如果我注释掉格式化步骤,UITableView 会在一秒钟左右加载。

标签: objective-c ios uitableview nsxmlparser


【解决方案1】:

你的算法不是很好。对于每个标签,您都尝试将其删除,即使它已经被剥离。此外,循环的每次迭代都会生成整个 HTML 字符串的副本,通常甚至不会删除任何内容。如果您不使用 ARC,这些副本也将持续存在,直到当前的自动释放池被弹出。你不仅在浪费内存,还做了很多不必要的工作。

测试您的方法(使用 Cocoa 维基百科文章)需要 3.5 秒。

这是此代码的改进版本:

- (NSString *)flattenHTML:(NSString *)html {

    NSScanner *theScanner = [NSScanner scannerWithString:html];
    theScanner.charactersToBeSkipped = nil;

    NSMutableString *result = [NSMutableString stringWithCapacity: [html length]];

    while (![theScanner isAtEnd]) {
        NSString *part = nil;
        if ([theScanner scanUpToString:@"<" intoString: &part] && part) {
            [result appendString: part];
        }
        [theScanner scanUpToString:@">" intoString:NULL];
        [theScanner scanString: @">" intoString: NULL];
    }
    return [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

这将告诉扫描器获取每个字符直到第一个 &lt; 并将它们附加到结果字符串(如果有)。然后它将跳到下一个&gt;,然后也跳过&gt; 以去除标签。这将重复直到文本结束。每个字符只被触摸一次,这使得它成为O(n) 算法。

同样的数据只需要 6.5 毫秒。这大约快 530 倍。

顺便说一句,这些测量是在 Mac 上进行的。 iPhone 上的确切值当然会有所不同。

【讨论】:

  • 斯文,你做到了!它现在加载大约 5-7 秒。一个巨大的进步。非常感谢。
【解决方案2】:

我不确定到底是什么问题?是不是 flattenHTML 方法需要很长时间才能完成?或者它在运行时阻止了您的应用程序?

如果最后一个问题是您的问题,并假设您在 flattenHTML 中做的所有事情都正确,并且确实需要很长时间才能完成。您唯一能做的就是确保在执行此操作时没有阻塞主线程。您可以使用 GCD 或 NSOperation 来实现这一点,除了让用户知道您现在正在解析数据并让他决定是否要等待或取消操作并执行其他操作之外,您无能为力。

【讨论】:

    【解决方案3】:

    我遇到了类似的问题,我不能让它更快。而不是这个,我展示了进度条来展示解析过程是如何完成的。

    下面的代码是其中的一部分。

    // at first, count the lines of XML file
    NSError *error = nil;
    NSString *xmlFileString = [NSString stringWithContentsOfURL:url
                                                       encoding:NSUTF8StringEncoding
                                                          error:&error];
    _totalLines = [xmlFileString componentsSeparatedByString:@"\n"].count;
    
    // do other things...
    
    // delegate method when the parser find new section
    - (void)parser:(NSXMLParser *)parser 
    didStartElement:(NSString *)elementName 
      namespaceURI:(NSString *)namespaceURI 
     qualifiedName:(NSString *)qName 
        attributes:(NSDictionary *)attributeDict
    {
        // do something ...
    
        // back to main thread to change app appearance
        NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
        [mainQueue addOperationWithBlock:^{
    
            // Here is important. Get the line number and update the progress bar.
            _progressView.progress = (CGFloat)[parser lineNumber] / (CGFloat)_totalLines;
        }];
    }
    

    我在 GitHub 中有示例项目。您可以下载并运行它。希望我的代码对你有所帮助。

    https://github.com/weed/p120727_XMLParseProgress

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-14
      • 2015-01-27
      • 2021-06-04
      • 2016-11-09
      • 2021-06-21
      • 2012-05-11
      • 2023-04-07
      • 1970-01-01
      相关资源
      最近更新 更多