Ios 快速文本绘制期间应用程序挂起

Ios 快速文本绘制期间应用程序挂起,ios,multithreading,uitableview,nsoperationqueue,terminal-emulator,Ios,Multithreading,Uitableview,Nsoperationqueue,Terminal Emulator,我正在使用终端仿真应用程序通过telnet连接到我的unix服务器,我正在使用tableview(一个单元格对应一个文本行)。让我烦恼的是,当服务器发送大量文本数据以响应某个命令时,这会使应用程序对触摸屏幕上的任何按钮或任何手势的响应变差(延迟),因为绘图处于主线程中,不允许其他进程工作,除非它完成 解决这个问题的最佳方法应该是什么?我是否需要OperationQueue,我对此不太了解 我需要的东西,如绘图时,我可以顺利地与应用程序交互(这可以暂停绘图) 我认为drawRect可能需要更长的时

我正在使用终端仿真应用程序通过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];
}
执行文本替换的其他方法如下所示-

-(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];
    }
}
-(void)初始化WithStringChar:(NSString*)charString
{
NSInteger len=charString.length;
而(len>0){
//可以显示长度。
NSInteger cutLength=totalCols-cursorX+1;
if(长度<切割长度){
切割长度=长度;
}
NSString*str=[charString substringToIndex:cutLength];
[self-PlaceTextInrurentTextLine:str:YES:YES:YES];
charString=[charString substringFromIndex:cutLength];
len-=切割长度;
}
}
-(void)placeTextInCurrentTextLine:(NSString*)text:(BOOL)needsToReplace:(BOOL)UseCurrentCharacters:(BOOL)adjustCursor{
TextLineCell*TextLineCell=(TextLineCell*)[self.telnet_TableView cellForRowAtIndexPath:currentlineindexath];
如果(光标>总计){
如果(self.autowrap==是){
[自定线和设置光标:是];
textLineCell=(textLineCell*)[self.telnet_TableView cellForRowAtIndexPath:currentlineindexath];
}
其他的
游标--;
}
NSAttributedString*属性文本;
如果(使用当前字符){
//使用当前字符属性
attrText=[[NSAttributedString alloc]initWithString:text属性:CurrentCharacterDict];
}
否则{
//使用默认字符属性
attrText=[[NSAttributedString alloc]initWithString:text属性:DefaultCharacterDict];
}
NSMutableAttributeString*attrLineText=[telnet_ScreenData objectAtIndex:currentLineIndexPath.row];
如有需要(存放地点){
//用现有的字符替换字符
[attrLineText-ReplaceCharactersRange:NSMakeRange(cursorX-1,text.length)和AttributeString:attrText];
}
否则{
//插入和移位字符
[attrLineText InsertAttributeString:attrText-atIndex:cursorX-1];
//移出最后的字符
attrLineText=[[NSMutableAttributedString alloc]initWithAttributedString:[attrLineText attributedSubstringFromRange:NSMakeRange(0,totalCols)];
}
[telnet_ScreenData replaceObjectAtIndex:currentLineIndexPath.row with Object:attrLineText];
如果(调整光标){
cursorX=cursorX+(int)text.length;
}
if(textLineCell){
textLineCell.textLineText=attrLineText;
[textLineCell设置需要显示];
}
否则{
[\u telnet\u TableView ScrollToRowatineXpath:homeIndexPath atScrollPosition:UITableViewScrollPositionTop动画:是];
}
}

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

当输入文本时,您应该确定哪些文本在哪行上,然后调用
insertRowsAtIndexPaths:withRowAnimation:
告诉表视图有新数据。然后等待它询问您特定的行,当它询问时,您为它配置一个单元格。您不维护表视图单元格的集合。您只需配置所需的一个单元格,理想情况下,使用
dequeueReusableCellWithIdentifier:forIndexPath:
从空闲列表中提取一个现有的单元格。在任何给定的时间,存在的单元格数量应该与屏幕上的行数量相同(可能加上一些滚动单元格)。研究细节


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

所有性能问题的主要问题是实际问题发生在哪里。你需要先用时间分析器运行仪器,看看你在哪里花费时间。然后你可以研究如何改进它

这就是说,有几个相当明显的事情你可能做得不正确。首先,必须正确管理表单元重用。在
cellForRowAtIndexPath:
中,您应该获取一个以前使用过的单元格,该单元格已从屏幕上滚动下来(使用
dequeueReusableCellWithIdentifier:
),然后简单地用新数据重新配置它。您不应该在每次调用
cellforrowatinexpath:
时都创建新的单元格。您应该使单元格尽可能简单(您可能只需要修改其文本的单个
UILabel

您还必须非常小心,避免调用
reloadData
。既然你要去j