-1

我正在开发终端仿真应用程序以通过 telnet 连接到我的 unix 服务器,我正在使用 tableview(一个单元格用于一个文本行)。令我困扰的是,当服务器发送大量文本数据以响应某些命令时,这会使应用程序对触摸屏幕上的任何按钮或任何手势的响应速度降低(延迟),因为绘图位于主线程中,除非它允许其他进程工作完成的。

什么应该是解决此问题的完美方法,我是否需要 OperationQueue,我对此了解不多。

我需要一些东西,比如在绘图时,我可以与应用程序顺利交互(这可以暂停绘图)。

我认为drawRect需要更长的时间,实际上我在做的不是reloadData或ReloadTableCell,我在tableview的文本行单元格中有一个可变的属性字符串,当需要将文本添加到该单元格时,我只是替换该可变属性字符串中的字符并为调用drawRect的单元格调用setNeedsDisplay。在 drawRect 我的代码如下 -

-(void)drawRect:(CGRect)rect
{
    _cursorView.frame = CGRectMake(([_delegate cursorXInTextLine:self] - 1 )* 6, 0, 6, 10);

    [self drawText:0 yPosition:0 canvasWidth:self.bounds.size.width canvasHeight:10];
}


- (void)drawText:(CGFloat)xPosition yPosition:(CGFloat)yPosition canvasWidth:(CGFloat)canvasWidth canvasHeight:(CGFloat)canvasHeight
{
    //Draw Text
    CGRect textRect = CGRectMake(xPosition, yPosition, canvasWidth, canvasHeight);

    [self.textLineText drawInRect: textRect];
}

执行 textReplacement 的其他方法如下 -

-(void)initializeWithStringChar:(NSString*)charString
{
    NSInteger len = charString.length;

    while (len > 0) {

        //length can be displayed.
        NSInteger cutLength = totalCols - cursorX + 1;

        if (len < cutLength) {
            cutLength = len;
        }

        NSString *str = [charString substringToIndex:cutLength];
        [self placeTextInCurrentTextLine:str :YES :YES :YES];

        charString = [charString substringFromIndex:cutLength];
        len -= cutLength;
    }
}
-(void)placeTextInCurrentTextLine:(NSString *)text :(BOOL)needsToReplace :(BOOL)useCurrentCharAttrs :(BOOL)adjustCursor{

    TextLineCell *textLineCell = (TextLineCell *)[self.telnet_TableView cellForRowAtIndexPath:currentLineIndexPath];

    if (cursorX > totalCols) {
        if (self.autowrap == YES) {
            [self goNewLineAndResetCursor:YES];

            textLineCell = (TextLineCell *)[self.telnet_TableView cellForRowAtIndexPath:currentLineIndexPath];
        }
        else
            cursorX--;
    }

    NSAttributedString *attrText;

    if (useCurrentCharAttrs) {
        //Use current chars attributes
        attrText = [[NSAttributedString alloc]initWithString:text attributes:currentCharAttrDict];
    }
    else{
        //Use default chars attributes
        attrText = [[NSAttributedString alloc]initWithString:text attributes:defaultCharAttrDict];
    }

    NSMutableAttributedString *attrLineText = [telnet_ScreenData objectAtIndex:currentLineIndexPath.row];

    if (needsToReplace) {
        //Replace chars with existing
        [attrLineText replaceCharactersInRange:NSMakeRange(cursorX - 1, text.length) withAttributedString:attrText];
    }
    else{
        //Insert and shift chars
        [attrLineText insertAttributedString:attrText atIndex:cursorX - 1];

        //shift out the last characters
        attrLineText = [[NSMutableAttributedString alloc]initWithAttributedString:[attrLineText attributedSubstringFromRange:NSMakeRange(0, totalCols)]];
    }

    [telnet_ScreenData replaceObjectAtIndex:currentLineIndexPath.row withObject:attrLineText];

    if (adjustCursor) {
         cursorX = cursorX + (int)text.length;
    }

    if (textLineCell) {
        textLineCell.textLineText = attrLineText;

        [textLineCell setNeedsDisplay];
    }
    else{
        [_telnet_TableView scrollToRowAtIndexPath:homeIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }
}
4

1 回答 1

1

编辑:(placeTextInCurrentTextLine::::这是一个糟糕的方法名称)通过调用直接编辑表格视图单元格cellForRowAtIndexPath:。这迫使系统创建一个可能不在屏幕上的单元格(甚至可能弹出一个缓存中的单元格)。如果生成了大量的单元格,这实际上可能会强制创建原本永远不需要的单元格。数据源的工作是被动并在请求时配置单元格。

当文本进入时,您应该弄清楚哪些文本在哪些行上,然后调用insertRowsAtIndexPaths:withRowAnimation:以告诉表格视图有新数据。然后,您等待它询问您特定的行,当它询问您时,您为它配置一个单元格。您不需要维护表格视图单元格的集合。您只需配置您被要求的一个单元格,理想情况下使用dequeueReusableCellWithIdentifier:forIndexPath:. 在任何给定时间,存在的单元格应该只与屏幕上的行数一样多(可能加上一些用于滚动的行)。研究使用数据填充动态表视图以获取详细信息。


表格视图是一个有趣的解决方案。我会选择UITextView,因为它的设计更适合处理大文本块。但是谁知道呢,tableview 可能是一个相当聪明的解决方案(我可以看到它甚至可能比我的方式更好地工作)。

所有性能问题的主要问题是实际问题发生在哪里。您需要首先使用 Time Profiler 运行 Instruments,然后查看您将时间花在哪里。然后你可以研究如何改进它。

也就是说,有几件相当明显的事情你可能做错了。首先,您必须正确管理表格单元格的重用。在cellForRowAtIndexPath:您应该获取已滚动出屏幕(使用dequeueReusableCellWithIdentifier:)的先前使用的单元格,然后简单地用您的新数据重新配置它。您应该在每次调用cellForRowAtIndexPath:. 您应该使您的单元格尽可能简单(您可能只需要一个UILabel您修改其文本的单元格)。

您还必须非常小心避免调用reloadData。由于您将继续添加到末尾,因此您想调用insertRowsAtIndexPaths:withRowAnimation:. 这种方式UITableView不必重新计算所有内容。

在后端,如果您发现将传入流量解析为行的成本很高,那么您绝对应该将解析移出主队列。原则上,NSOperationQueue是“最高抽象”,通常最好使用可用的最高抽象。也就是说,我发现大多数人比操作队列更容易“获取”调度队列。大部分操作队列 API 都基于 GCD 之前的并发系统,因此很多文档都涵盖了当今很少使用的主题。我建议阅读并发编程指南的调度队列部分来熟悉它们。管理调度队列是 iOS 开发人员的一项关键且非常常见的技能。

但是第一步,一如既往地处理性能,是熟悉 Instruments 并确保您了解程序中实际发生的情况。

于 2015-02-20T15:09:32.127 回答