2013-07-11 26 views
0

我在后台线程中运行数学计算。试图在UITextView中实时发布结果。但是,直到后台线程完成后才会显示结果。为什么不?当后台线程繁忙时UITextView不会滚动

我在后台揭开序幕的方法,

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^() { 
    [self v2]; 
}); 

后台线程方法的形式为,

- (void) v2 { 
    NSString *result; 
    // ... loop a bunch of times generating lots of results 
    for (bunch of stuff to compute) { 
     // If using dispatch_async, nothing is displayed until this method finishes 
     // If dispatch_sync then it does display and update 
     result = [self computeNextValue]; 
     dispatch_async(dispatch_get_main_queue(), ^() { 
     textView.text = result; 
     }); 
    } // end computation 
} 

这实际上并没有,直到我开始尝试过太大的问题滚动视图。痛苦地缓慢。所以我创建了一个NSTimer来定期滚动UITextView。但是,即使计时器弹出并且该方法运行以请求滚动,UITextView也不会滚动,直到后台方法完成。

+0

您不在后台执行。当你阻塞主线程时,UI只会冻结。 – jakenberg

回答

0

首先,确保你在block中使用weakSelf而不是self。

__weak MyClass weakSelf = self; 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^() { 
    [weakSelf v2]; 
}); 

...

__weak MyClass weakSelf = self; 
dispatch_async(dispatch_get_main_queue(), ^() { 
    weakSelf.textView.text = result; 
}); 

但是,这不会导致延迟的更新。我非常怀疑队列的优先级DISPATCH_QUEUE_PRIORITY_LOW。请改用DISPATCH_QUEUE_PRIORITY_DEFAULT。

+0

试了两个。不用找了。 – David

0

我想我已经知道了,至少是凭经验。

它看起来像在UITextView上设置文本或以编程方式启动滚动设置在另一个线程中运行的动画序列。似乎正在发生的事情是,设置UITextView文本的请求比UITextView设置动画的能力快。当属性再次发生变化时,它显然会取消它在另一个线程上设置的早期动画。当我继续以变化的要求来淹没它时,在'文本'的价值再次发生变化之前,它永远无法完成它想要做的事情。

我的解决方案涉及多个步骤。

在我最初的方法中,我非常快速地将textView.text设置为一些新值。我设置了一个定时器来定期请求UITextView滚动。 在我的修改中,我计算结果字符串的新值,但不要将其设置为UITextView。相反,基于计时器定期在textview上设置文本。这允许文本视图追上。

但是,我注意到这仍然不可靠。如果我碰巧在滚动时再次设置文本,会出现奇怪的效果,例如非常慢的滚动。似乎滚动动画和文字的重复设置仍然造成问题。

所以解决这个问题,我创建了一个属性来指示视图是否滚动。将视图控制器设置为UITextField委托。当我请求视图滚动时,我将该标志设置为指示其滚动。如果尚未滚动,则只更新内容和请求滚动。结束了很好的工作。无论我设置定时器的速度有多快,它都会适当地等待。

// ViewController.h 
@property BOOL isViewScrolling; 

// ViewController.m 
// initialize property in viewDidLoad 
- (void)viewDidLoad { 
    self.isViewScrolling = FALSE; 
    textView.delegate = self; 
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(scrollIt) userInfo:nil repeats:TRUE]; 
} 

- (void)scrollIt { 
    NSLog(@"scrollit thread=%d", [[NSThread currentThread]isMainThread]); 
    if (!self.isViewScrolling) { 
    textView.text = self.result; 
    NSRange range = NSMakeRange(textView.text.length - 1, 1); 
    self.isViewScrolling = TRUE; 
    [textView scrollRangeToVisible:range]; 
    } 
} 

// UITextView delegate 
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { 
    NSLog(@"Animation stopped"); 
    self.isViewScrolling = FALSE; 
} 
0

尝试改变所述预定调度队列串结束与背景。

__weak MyClass weakSelf = self; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^() { 
     weakSelf.textView.text = result; 
    }); 

而且你应该添加一个UIActivityIndi​​cator,并开始在操作开始动画,然后停止动画的textview.text领域已更新后。

这是一个很好的功能,显示用户目前还正在做的过程

另外我想从NSThread远离我读过来自苹果的几个论坛和文档是强调使用GCD和块操作。

相关问题