以可重用的方式提取dispatch_async调用

时间:2013-02-12 02:29:15

标签: ios

我正在努力改进创建更多可重用的代码片段。目前在我们的应用程序中,我们有一个DataManager单例,所有对数据库的调用都通过。因此,对于对数据库的昂贵查找,我想将该调用放在嵌套的dispatch_async块中以不阻塞主线程。目前,

在ViewControllerA中:

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{
  NSArray *array = [DataManager myExpensiveMethodCall];
  dispatch_async(dispatch_get_main_queue(), ^{
    [self setEvents:array];
  });
});

我想知道是否有办法更好地抽象出这种“模式”。我已经在Apple的示例代码中看到了它,而且每当我需要执行昂贵的方法,然后更新UI时,我基本上都会执行这种类型的嵌套dispatch_async调用。我只是好奇,如果把这种类型的代码放在ViewController需要做的事情上,或者如果有更好的方法那么“好”。感谢。

3 个答案:

答案 0 :(得分:3)

在.h文件中将它放在任何@interface

之外
typedef void(^MyBlockType)(void);

在你的.m文件中,你可以使用像这样的东西

+(void)doAsyncWithBlock:(MyBlockType)asyncBlock andSyncBlock:(MyBlockType)syncBlock
{
    dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(aQueue, ^{
        if( asyncBlock != nil )
        {
            asyncBlock();
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            if( syncBlock != nil )
            {
                syncBlock();
            }
        });
    });
}

答案 1 :(得分:0)

是的,它可以做到这一点,我看到的主要优点是你可以消除对GCD的依赖,并使你的大部分代码与你的主要并发技术保持一致。

我认为开始一个负责管理异步工作单元调度和协调其回调的新类是合理的,而不是将其添加到视图控制器类中。这样的对象以后可以维护自定义调度队列或NSOperationQueue或未来的任何技术。我想象一下默认实例,它也提供了一个API来调度与底层线程技术无关的块。使用GCD的一个可能示例如下:

typedef void(^AsynchronousWorkManagerBlock)();

@interface AsynchronousWorkManager : NSObject

+ (AsynchronousWorkManager *)defaultManager;

- (void)executeInBackground:(AsynchronousWorkManagerBlock)backgroundBlock
     withMainThreadCallback:(AsynchronousWorkManagerBlock)callbackBlock;

@end


@implementation AsynchronousWorkManager

+ (AsynchronousWorkManager *)defaultManager
{
    static dispatch_once_t onceToken;
    static AsynchronousWorkManager *DefaultManager = nil;
    dispatch_once(&onceToken, ^{
        DefaultManager = [[self alloc] init];
    });
    return DefaultManager;
}

- (void)executeInBackground:(AsynchronousWorkManagerBlock)backgroundBlock
     withMainThreadCallback:(AsynchronousWorkManagerBlock)callbackBlock
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        backgroundBlock();
        dispatch_async(dispatch_get_main_queue(), callbackBlock);
    });
}

@end

客户端代码可以像这样调用它:

[[AsynchronousWorkManager defaultManager] executeInBackground:^{
                                            NSLog(@"This code is happening in the background");
                                        }
                                      withMainThreadCallback:^{
                                            NSLog(@"This code is happening on the main thread");
                                        }];

[[AsynchronousWorkManager defaultManager] executeInBackground:^{
                                            sleep(3);
                                        }
                                      withMainThreadCallback:^{
                                          NSLog(@"Done Sleeping");
                                      }];

稍后,如果您决定切换到使用NSOperationQueue而不是GCD,则可以直接更改而无需更改API,如以下示例实现中所示:

@interface AsynchronousWorkManager ()

@property (nonatomic, strong) NSOperationQueue *operationQueue;

@end


@implementation AsynchronousWorkManager

+ (AsynchronousWorkManager *)defaultManager
{
    static dispatch_once_t onceToken;
    static AsynchronousWorkManager *DefaultManager = nil;
    dispatch_once(&onceToken, ^{
        DefaultManager = [[self alloc] init];
    });
    return DefaultManager;
}

- (id)init
{
    if (self = [super init]) {
        _operationQueue = [[NSOperationQueue alloc] init];
    }

    return self;
}

- (void)executeInBackground:(AsynchronousWorkManagerBlock)backgroundBlock
     withMainThreadCallback:(AsynchronousWorkManagerBlock)callbackBlock
{
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:backgroundBlock];
    blockOperation.completionBlock = ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:callbackBlock]; };

    [self.operationQueue addOperation:blockOperation];
}

@end

我并不完全确定这是多么必要,但是因为它可以在没有太多代码的情况下完成,我认为它可能是值得的,而不仅仅是过度工程的练习。如果您的应用程序广泛使用不同的优先级队列或更专业的GCD功能,我可能会因为在没有真正获得任何底层实现灵活性的情况下引入泄漏抽象而转向此。

答案 2 :(得分:0)

我通常定义这两个函数。它们使代码更紧凑,并遵循我不想阻塞主线程的模式(因此RUN_ON_BACKGROUND_THREAD调用是异步的)但是可以阻止后台线程以更新主线程(因此RUN_ON_UI_THREAD)调用是同步的。它还通过允许直接执行调用(如果从主线程调用它)来避免死锁。

void RUN_ON_UI_THREAD(dispatch_block_t block)
{
    if ([NSThread isMainThread])
        block();
    else
        dispatch_sync(dispatch_get_main_queue(), block);
}

void RUN_ON_BACKGROUND_THREAD(dispatch_block_t block)
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}