iOS:如何调试UI延迟[iPad2双核]

Jay*_*hao 5 multithreading ios

在我的应用程序中,我必须处理来自网络的大量数据并将其解析为优化的本地格式,然后将它们保存到数据库或将其发送到UI(如果有任何UI正在等待该数据).

我知道在主线程中进行大量的解析工作是愚蠢的,因为它会阻塞主线程并使UI交互非常激动.

所以这就是我试图确保UI线程是免费的:

  • 全局优化,以删除真正慢的代码,尽管它是什么线程.
  • 降低所有线程优先级,即

    NSOperation.threadPriority=0.1;
    NSThread.threadPriority=0.1;
    dispatch_async(dispatch_get_global_queue(PRIORITY_LOW));
    
    Run Code Online (Sandbox Code Playgroud)

在这两个步骤之后,当在后台线程中完成一些繁重的工作时,UI会更加顺畅,但仍然非常激动.

这个bug看起来像这样:当我滚动一张桌子时非常流畅,突然桌子冻结了大约0.1秒,然后像往常一样顺利地继续.在冻结期间,我可以看到仪器中的CPU爆发.但是在单独的线程视图中,我可以看到主线程正常,几乎没有CPU消耗.

  1. 那么为什么UI表仍然会冻结?
  2. 为什么后台线程仍然会在如此低的线程优先级设置下占用如此多的CPU周期?
  3. 你们如何确保UI总是响应?
  4. 是否有任何有用的工具来突出导致UI冻结的斑点?

jha*_*ott 3

一个好的解决方案是将Grand Central Dispatch (GCD) 和Objective-C 块与本地缓存结合使用。当您的表格单元格加载时,您应该尽快从tableView:cellForRowAtIndexPath:已设置有任何本地保存数据的表格单元格返回。单元格中需要下载数据或进行大量处理的任何部分都应该有一个占位符(例如活动指示器)。繁重的处理或下载将发生在另一个线程上,如下所示:

// ...This code is at the end of tableView:cellForRowAtIndexPath: where cell
// is already set up and you should have access to your cache and your data
// manager object that will do the processing or downloading for you.

// Show the placeholder (it will hide when real data is set)
cell.placeholder.hidden = NO;
// Check if item is local
MyDataItem* item = [myCache objectForKey:itemKey];
if(item != nil)
{
    // Item is available locally, set up the cell now
    cell.someImageView.image = item.image;
    cell.someLabel.text = item.text;
    cell.placeholder.hidden = YES;
}
else
{
    // Item is not in cache, go and get it ready
    [myDataManager goAndGet:itemKey completion:^(MyDataItem* theItem)
    {
        // Put the item in the cache
        [myCache setObject:theItem forKey:itemKey];
        // This block will be executed later and the cell may have been reused
        // by then, so we need to ask the table view directly for the cell.
        // This returns nil if cell is not currently visible.
        MyTableCell* theCell = (MyTableCell*)[tableView cellForRowAtIndexPath:indexPath];
        // Set up the table cell
        if(theCell != nil)
        {
            myCell.someImageView.image = theItem.image;
            myCell.someLabel.text = theItem.text;
            myCell.placeholder.hidden = YES;
        }
    }];
}
return cell;
Run Code Online (Sandbox Code Playgroud)

正如您从上面的代码中看到的,即使需要进行一些繁重的处理,单元格也会很快返回。这使 UI 保持良好且响应灵敏,不会出现任何导致表格视图上滚动不稳定的延迟。数据管理器将使用 GCD 来执行处理,如下所示:

- (void)goAndGet:(NSString*)itemKey completion:(void (^)(MyDataItem* theItem))completionBlock
{
    // Note: this uses DISPATCH_QUEUE_PRIORITY_DEFAULT - but you might want to use
    // a different queue, probably one you created yourself for these tasks.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
    {
        // Do heavy processing or downloading here
        MyDataItem* item = [heavyProcessor doProcessingForItem:itemKey];

        // The completion block must happen on the main queue
        dispatch_async(dispatch_get_main_queue(), ^
        {
            // Call the completion block
            completionBlock(item);
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

请注意,上面的所有代码都假设您使用的是ARC :)