0

我正在后台线程中运行数学计算。尝试在 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 也不会滚动,直到后台方法完成。

4

3 回答 3

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 和块操作的使用。

于 2013-07-12T04:59:03.223 回答
0

首先,确保您在块中使用的是 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。

于 2013-07-12T00:06:13.107 回答
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;
}
于 2013-07-12T01:44:35.710 回答