NSTextView不使用Grand Central Dispatch更新

时间:2019-03-08 14:14:08

标签: grand-central-dispatch nstextview dispatch-async

我需要显示一个大文件的加载百分比,而不会阻塞用户界面。我进行了一些研究,找到了使用同一代码的使用Grand Central Dispatch的几种解决方案。但不幸的是,它对我不起作用。

要运行测试,我用一个while循环做了一个简单的例子,该循环用一个百分比更新了NSTextView。

当我单击开始按钮时,NSTextView用随机值更新一次,然后出现OSX旋转的色轮,并且该界面被阻止。当我单击“停止”按钮时,没有任何反应。缺点是,一旦循环结束,NSTextView将显示100,控制台将显示两次“ Stop”(停止)字样,就好像两次单击都将其保留在内存中一样。

由于几个答案使用的GCD代码似乎对其他人有用,所以我认为问题可能出在硬件上。我使用的是2009年运行OSX Mountain Lion的iMac。在终端中,物理和逻辑cpu的数量为:

sysctl hw.physicalcpu:2

sysctl hw.logicalcpu:2

在此先感谢您的帮助。这是我的代码:

@interface MainViewController ()
{
    NSTextView *label;
    NSButton *startButton;
    NSButton *stopButton;
}
@end

@implementation MainViewController

- (void)loadView
{
    NSView *aView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 500, 500)];
    [aView setWantsLayer:YES];
    aView.layer.backgroundColor = [NSColor lightGrayColor].CGColor;
    self.view = aView;

    label = [[NSTextView alloc] initWithFrame:NSMakeRect(30, 450, 100, 25)];
    label.backgroundColor = [NSColor whiteColor];
    label.textColor = [NSColor blackColor];
    [label setEditable:NO];
    label.font = [NSFont fontWithName:@"Arial" size:18.0];
    [label setString:@"0"];

    startButton = [[NSButton alloc] initWithFrame:NSMakeRect(30, 350, 60, 30)];
    [startButton setBezelStyle:NSSegmentStyleTexturedRounded];
    startButton.title = @"Start";
    startButton.font = [NSFont fontWithName:@"Arial" size:14.0];
    [startButton setTarget:self];
    [startButton setAction:@selector(start)];

    stopButton = [[NSButton alloc] initWithFrame:NSMakeRect(30, 300, 60, 30)];
    [stopButton setBezelStyle:NSSegmentStyleTexturedRounded];
    stopButton.title = @"Stop";
    stopButton.font = [NSFont fontWithName:@"Arial" size:14.0];
    [stopButton setTarget:self];
    [stopButton setAction:@selector(stop)];

    [self.view addSubview:label];
    [self.view addSubview:startButton];
    [self.view addSubview:stopButton];
}

- (void)start
{
    NSLog(@"Start");

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
    {
        //Background thread
        float total = 200000;

        for(int i = 0; i < total; i++)
        {
            //do some hard work here...
            float percent = ((float)i/total) * 100;

            dispatch_async(dispatch_get_main_queue(), ^(void)
            {
                //Main thread (UI update)
                [label setString:[NSString stringWithFormat:@"%.f", percent]];
                [label setNeedsDisplay:YES];
            });
        }
    });

    NSLog(@"END : Start");
}

- (void)stop
{
    NSLog(@"Stop");
}

1 个答案:

答案 0 :(得分:0)

我怀疑您的样本中的for循环非常热-即它几乎以最快的速度在您的主队列上调用dispatch_async,这使UI更新线程饱和。

这是一个非常愚蠢的建议,但是您可以尝试在分派到主线程之前进行sleep调用吗?只需10毫秒左右。这样可以让UI时间在下一次异步调用点击之前进行更新。

如果您在注释“在这里努力工作”的代码花费了足够的时间来允许UI更新并接收其他事件,那么您就可以了。我认为这只是使您的用户界面变得饱和的硬编码示例。