0

我有一个方法,我添加到我创建的 GCD 队列中(所以它是一个串行队列),然后异步运行它。从该代码块中,我向主队列进行调度,当调度到主队列的代码块完成时,我将 BOOL 标志设置为 YES,以便我在代码中进一步检查此条件是否为 YES然后我可以继续下一个方法。这是简短的代码:

dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);

dispatch_async(queue, ^{

        Singleton *s = [Singleton sharedInstance];

        dispatch_sync(dispatch_get_main_queue(), ^{
            [s processWithCompletionBlock:^{

                // Process is complete
                processComplete = YES;
            }];
        });
});

while (!processComplete) {

        NSLog(@"Waiting");
}

NSLog(@"Ready for next step");

但是这不起作用,因为 dispatch_sync 永远无法在主队列上运行代码。这是因为我在主队列上运行了一个 while 循环(使其忙碌)?

但是,如果我将 while 循环的实现更改为:

while (!processComplete) {

        NSLog(@"Waiting")
        NSDate *date = [NSDate distantFuture];
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date];
}

它可以正常工作。对于这种情况,这是一个可接受的解决方案吗?我可以用其他首选方式吗?有什么神奇的东西可以NSRunLoop做?我需要更好地理解这一点。

4

3 回答 3

2

主线程的 NSRunLoop 作业的一部分是运行在主线程上排队的任何块。通过在 while 循环中旋转,您可以阻止 runloop 继续进行,因此除非您自己明确地让循环运行,否则排队的块永远不会运行。

Runloops 是 Cocoa 的基本组成部分,文档非常好,所以我建议阅读它。

作为一项规则,我会避免像您那样手动调用运行循环。如果您有多个手动调用在彼此之上运行,您将浪费内存并使事情变得非常复杂。

但是,有一种更好的方法可以做到这一点。将您的方法拆分为 -process 和 -didProcess 方法。使用您的 -process 方法启动异步操作,完成后,从完成块调用 -didProcess。如果您需要将变量从一种方法传递给另一种方法,您可以将它们作为参数传递给您的 -didProcess 方法。

例如:

dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);

dispatch_async(queue, ^{
        Singleton *s = [Singleton sharedInstance];

        dispatch_sync(dispatch_get_main_queue(), ^{
            [s processWithCompletionBlock:^{
                [self didProcess];
            }];
        });
});

你也可以考虑让你的单例拥有调度队列并让它负责处理 dispatch_async 的东西,因为如果你总是异步使用它,它会节省所有那些讨厌的嵌入块。

例如:

[[Singleton sharedInstance] processAyncWithCompletionBlock:^{
   NSLog(@"Ready for next step...");
   [self didProcess];
}];
于 2012-12-11T17:22:52.763 回答
1

做类似你发布的事情很可能会冻结用户界面。与其冻结所有内容,不如在完成块中调用您的“下一步”代码。

例子:

dispatch_queue_t queue = dispatch_queue_create("ProcessSerialQueue", 0);
dispatch_queue_t main = dispatch_get_main_queue();

dispatch_async(queue, ^{

        Singleton *s = [Singleton sharedInstance];

        dispatch_async(dispatch_get_main_queue(), ^{
            [s processWithCompletionBlock:^{
                // Next step code
            }];
        });
});
于 2012-12-11T16:35:45.997 回答
-2

不要像在块内等待值那样创建循环,块中的变量是只读的,而是从块内调用您的完成代码。

dispatch_async(queue, ^{
    Singleton *s = [Singelton sharedInstance];
    [s processWithCompletionBlock:^{
        //process is complete
        dispatch_sync(dispatch_get_main_queue(), ^{
            //do something on main queue....
            NSLog(@"Ready for next step");
        });
    }];
});
NSLog(@"waiting");
于 2012-12-11T16:52:57.090 回答