大中央派遣和职能 [英] Grand Central Dispatch and functions

查看:61
本文介绍了大中央派遣和职能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在查看这个问题以尝试解决了我在此处的问题. tl; dr是我要使用GCD在执行某些任务时显示等待"屏幕,然后在完成后隐藏该屏幕.现在,我有

I've been looking at this question to try to solve the problem I have here. The tl;dr is I want to use GCD to let me show a "Waiting" screen while I preform some tasks, then hide the screen when it's done. Right now, I have

- (void) doStuff
{
    // Show wait on start
    [self.waitScreen setHidden:NO];

    dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null);
    dispatch_async(queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            // Double nesting the dispatches seems to allow me to do UI changes as part of 'Code to execute' below.
            // If I do not double nest like this, the UI still freezes while it executes
            dispatch_queue_t queue2 = dispatch_queue_create("com.myDomain.myApp",null);
            dispatch_async(queue2, ^{
                dispatch_async(dispatch_get_main_queue(), ^{

                    // Code to execute
                    {
                        //... Do my time consuming stuff here ...
                        // For testing purposes, I'm using
                        int i = 0;
                        while (i < 1000000000)
                        {
                            i++;
                        }
                    }

                    // Hide Wait Screen on End
                    [self.waitScreen setHidden:YES];
                });
            });
        });
    });
}

这正是我想要的.我这样叫[self doStuff]

And this works just how I want. I'm calling [self doStuff] like so

- (IBAction) buttonTouchUpInside:(id)sender
{
    [self doStuff];
}

- (void) doStuff
{
    // ... code from first code block here ...
}

一切到此为止都是完美的.现在,我发现我需要在函数调用中使用它.所以我需要类似的东西:

Everything to this point works perfectly. Now, I've discovered I will need to use this in a function call. So I need something like:

- (IBAction) buttonTouchUpInside:(id)sender
{
    NSMutableString *string= [self doStuff];

    // ... use 'string' to do other stuff ...
    // For testing, I'm using
    self.label.text = string;
}

- (NSMutableString *) doStuff
{
    // ... code from first code block here ...
}

我如何更改语法以便能够通过dispatch_async传递变量?

How do I need to change the syntax to be able to pass variables around with dispatch_async?

我查看了 Apple Docs 并尝试

- (IBAction) buttonTouchUpInside:(id)sender
{
    NSMutableString *string= [self doStuff];

    // ... use 'string' to do other stuff - shows 'nil' when I put breakpoints here ...
    // For testing, I'm using
    self.label.text = string;
}

- (NSMutableString *) doStuff
{
    __block NSMutableString *string = [[NSMutableString alloc] initWithString:@"Initing"];

    // Show wait on start
    [self.waitScreen setHidden:NO];

    dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null);
    dispatch_async(queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null);
            dispatch_async(queue, ^{
                dispatch_async(dispatch_get_main_queue(), ^{

                    // Code to execute
                    {
                        int i = 0;
                        while (i < 1000000000)
                        {
                            i++;
                        }

                        [string setString:@"Hello World"];
                    }

                    // Hide Wait Screen on End
                    [self.waitScreen setHidden:YES];
                });
            });
        });
    });

    return string;
}

但是运行此命令时,label仅显示Initing.我需要它来显示Hello World.我在块中所做的任何更改都没有通过.

But when I run this, label just shows Initing. I need it to show Hello World. None of the changes I make in the block are being passed through.

看了其他一些问题之后,这似乎被称为比赛条件".据我了解,一旦命中dispatch_async,该块中的代码就开始在新线程上运行,但是该块外的代码继续在旧线程上同时运行.因此,看起来运行该块的线程可以到达[string setString:@"Hello World"];之前,该块外部的线程已到达self.label.text = string.如何让self.label.text = string行等到[string setString:@"Hello World"];完成?

After looking at some other questions, this seems to be referred to as a "race condition". As I understand it, once it hits the dispatch_async, the code in the block starts running on a new thread, but the code outside of the block continues to run at the same time on the old thread. So it looks like the thread outside the block is getting to self.label.text = string before the thread running the block can get to [string setString:@"Hello World"];. How can I make the self.label.text = string line wait until [string setString:@"Hello World"]; finishes?

推荐答案

首先,您对双重嵌套的推理存在缺陷.不确定为什么它可能会起作用,但是正确的方法是进行一些异步工作,并且每当您要更新ui时,都将该代码包装在主队列的一个块中.

First of all your reasoning of double nesting is flawed. Not sure why it might have worked, but the correct way is to do some async work, and any time you want to update the ui wrap that code in a block on the main queue.

- (void) doStuff
{
    // Show wait on start
    [self.waitScreen setHidden:NO];

    // queue should be a global variable, you don't want to create it every time you
    // execute doStuff
    dispatch_async(queue, ^{
        // Code to execute
        {
            //... Do my time consuming stuff here ...
            // For testing purposes, I'm using
            int i = 0;
            while (i < 1000000000)
            {
                i++;
            }
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            // Hide Wait Screen on End
            [self.waitScreen setHidden:YES];
        });
    });
}

由于您的队列正在异步执行工作,因此您不能简单地从doStuff返回一个值而无需等待,这将再次阻塞您调用doStuff的队列.

Since your queue is performing work asynchronously you can't simply return a value from doStuff without waiting, which will block the queue you call doStuff on again.

如果只想在标签上设置一个值,也可以在主队列上执行的块中执行此操作,例如隐藏等待屏幕.

If you just want to set a value on a label, do that too in the block executed on the main queue, like hiding the wait screen.

另一种常见的处理方法是提供一个回调块,以在工作完成后立即执行.

Another common way to do things it to provide a callback block to execute as soon as work is finished.

- (void) doStuffWithCompletionBlock:(void(^)(NSString *))block
{
    // again, a global variable for the queue
    dispatch_async(queue, ^{
        // do some work here that shouldn't block the UI

        dispatch_async(dispatch_get_main_queue(), ^{
            block(@"My result string");
        });
    });
}

- (void) myAction:(id)sender
{
    __weak typeof(self) weakSelf = self;
    [self doStuffWithCompletionBlock:^(NSString *result) {
        weakSelf.label.text = result;
    }];
}

请注意,我在主队列上调用了完成块,这是一种选择.您可以将其省略,但是稍后您仍可以在完成块本身中对主队列进行所有UI更新.

Notice that I call the completion block on the main queue, this is a choice. You could leave that out, but then you would still have do all UI updates on the main queue later in the completion block itself.

这篇关于大中央派遣和职能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆