dispatch_queue_t myQueue = dispatch_queue_create("com.mydomain.my-main-queue", NULL);
dispatch_set_target_queue(myQueue, dispatch_get_main_queue());
我的最终目标是使用队列作为NSOperationQueue的基础队列属性,因为 Apple 的文档明确声明不要使用 dispatch_get_main_queue()。尽管使用间接队列,但它在技术上遵循文档。
这一切的原因是因为NSOperationQueue.mainQueue 对于异步操作不是安全的,因为它是全局可访问的,并且它的maxConcurrentOperationCount 设置为 1。所以可以很容易地用这个操作队列来打自己的脚。
更新 1
关于这个问题假设“异步 NSOperation”是什么的基础,似乎有很多困惑。需要明确的是,这是基于此WWDC 会话中的概念。特定概念是使用“操作就绪”和依赖管理来管理应用程序中的任务,这意味着将异步 NSOperations 添加到 NSOperationQueues 以利用这一点。如果您将这些概念与这个问题的精神相结合,希望推理会更有意义,并且您可以专注于将解决方案与其他解决方案进行比较和对比。
更新 2 - 问题示例:
// VendorManager represents any class that you are not in direct control over.
@interface VendorManager : NSObject
@end
@implementation VendorManager
+ (void)doAnsyncVendorRoutine:(void (^)(void))completion {
// Need to do some expensive work, make sure we are off the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND 0), ^(void) {
// Some off main thread background work
sleep(10);
// We are done, go back to main thread
[NSOperationQueue.mainQueue addOperationWithBlock:completion];
});
}
@end
// MYAsyncBoilerPlateOperation represents all the boilerplate needed
// to implement a useful asnychronous NSOperation implementation.
@interface MYAlertOperation : MYAsyncBoilerPlateOperation
@end
@implementation MYAlertOperation
- (void)main {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:"Vendor"
message:"Should vendor do work?"
preferredStyle:UIAlertControllerStyleAlert];
__weak __typeof(self) weakSelf = self;
[alertController addAction:[UIAlertAction actionWithTitle:@"Yes"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[VendorManager doAnsyncVendorRoutine:^{
// implemented in MYAsyncBoilerPlateOperation
[weakSelf completeThisOperation];
}];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"No"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[weakSelf cancel];
}]];
[MYAlertManager sharedInstance] presentAlert:alertController animated:YES];
}
@end
// MYAlertOperation will never complete.
// Because of an indirect dependency on operations being run on mainQueue.
// This example is an issue because mainQueue maxConcurrentOperationCount is 1.
// This example would not be an issue if maxConcurrentOperationCount was > 1.
[NSOperationQueue.mainQueue addOperation:[[MYAlertOperation alloc] init]];
更新 3 - 示例 2:
我没有展示 MyAsyncBlockOperation 的实现,但您可以将其用作它在 Swift 中的基础。
// operation.asynchronous is YES, and things are implemented correctly for state changes.
MyAsyncBlockOperation *operation = [MyAsyncBlockOperation new];
__weak MyAsyncBlockOperation *weakOperation = operation;
// executionBlock is simply invoked in main
// - (void)main { self.executionBlock() };
operation.executionBlock = ^{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Vendor"
message:@"Should vendor do work?"
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"Yes"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"Never called");
[weakOperation completeWithSuccess];
}];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"No"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[weakOperation cancel];
}]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}];
operation.completionBlock = ^{
NSLog(@"If YES, Never called. If NO, called.");
};
[[NSOperationQueue mainQueue] addOperation:operation];
所以我想,为什么不用另一个 NSOperationQueue 呢?一个将其底层队列设置为前面提到的间接GCD 队列(仍然遵循文档)。所以我们可以有一个并发的 NSOperationQueue,合法地针对串行主GCD 队列,并最终确保操作在主线程上运行。
如果您需要澄清,请告诉我,这是完整代码的示例:
NSOperationQueue *asyncSafeMainQueue = [[NSOperationQueue alloc] init];
asyncSafeMainQueue.qualityOfService = NSQualityOfServiceDefault; // not needed, just for clarity
dispatch_queue_t underlyingQueue = dispatch_queue_create("com.mydomain.main-thread", NULL);
dispatch_set_target_queue(underlyingQueue, dispatch_get_main_queue());
asyncSafeMainQueue.underlyingQueue = underlyingQueue;
现在......有一个安全的操作队列,用于需要在主线程上运行的异步操作,并且没有任何不必要的上下文切换。
安全吗?