完成块?嵌入在同步工作流中的异步流程

时间:2012-08-29 18:15:04

标签: objective-c xcode block

长时间潜伏,第一次海报。我对Objective-C比较陌生,所以如果我问一些相当简单的事情我会道歉。我的谷歌& stack overflow-fu让我失望了,所以我觉得有人可能会帮忙。

我有一个同步过程,例如,连续执行三个函数 - 称之为A - > B-> C,执行任务A,然后是B,然后是C.

现在,B涉及一个带有委托回调的异步进程以完成。但是B必须在执行C之前完成,所以我需要一些机制,使得在B完成之前不会触发C.我想这个问题必须有一个共同的设计模式吗?

最初天真的解决方案是 -

执行A
执行B
而(!B完成){}
执行C

......但这看起来真的很蹩脚。

我怀疑我可以通过某种阻挡来做到这一点,但对于我的生活,我无法理解。有人可以帮忙吗?

感谢任何帮助!

纪尧姆

6 个答案:

答案 0 :(得分:4)

感谢所有的反馈 - 抱歉没有尽快回复。我现在以与建议略有不同的方式解决了这个问题:

首先,我将NSObject扩展为具有以下方法 -

#import "NSObject+LTExtensions.h"

@implementation NSObject (Testing)

- (void) performSelectorWithBlock: (SEL) selector withSemaphore:(dispatch_semaphore_t)semaphore
{
  [self performSelector:selector]; // This selector should complete the semaphore
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  dispatch_release(semaphore);
}

@end

这允许我通过选择器执行一个块。当块执行时,执行它的线程将等待,直到通过特定的调度信号量发出信号。

我们可以做的事情如下:

  • 致电A
  • 创建一个调度信号量并定义一个执行B
  • 的选择器
  • 调用上面定义的方法执行B并等待选择器完成
  • 当B完成时(通过委托回调),它通知发送信号量暂停等待
  • 然后我执行C

所以我们有

A
B -> Asynchronous with delegate callback
C 

以下是如何实施上述内容的简单示例

-(void) methodA {

  // ... do something

  // Assign your semaphore (this is a dispatch_semaphore_t)
  self.semaphore = dispatch_semaphore_create(0);
  [self performSelectorWithBlock:@selector(methodB) withSemaphore:semaphore];
  [self methodC];
}

-(void) methodB {
  // ... do whatever needs to be done asynchronously
  CFRunLoopRun();
}

-(void) methodBDelegateCallBack {
  // This is called when B completes

  // Signal completion
  dispatch_semaphore_signal(self.semaphore);
  CFRunLoopStop(CFRunLoopGetCurrent());
}

-(void) methodC {
 ...
}

工作得非常好,没有任何问题(但我是Obj C的新手,因此我的方法可能存在明显的问题。)

答案 1 :(得分:2)

此问题的另一种方法可能如下:为异步任务创建一个辅助对象,并在调用任务时复制完成块。完成异步任务后,使用委托方法调用完成块。因此,我们可以按如下顺序执行任务:

FSTask      *taskA = [FSTask taskWithName:@"Task A"];
FSAsyncTask *taskB = [FSAsyncTask asyncTaskWithName:@"Task B"];
FSTask      *taskC = [FSTask taskWithName:@"Task C"];


[taskA performTaskWithCompletionBlock:^ (NSString *result) {
    NSLog(@"%@", result);

    [taskB performTaskWithCompletionBlock:^ (NSString *result) {
        NSLog(@"%@", result);

        [taskC performTaskWithCompletionBlock:^ (NSString *result) {
            NSLog(@"%@", result);

        }];
    }];
}];

那么这是如何实现的?好吧,看看下面的任务对象......


FSTask.m - 在主线程上同步工作......

@interface FSTask ()

@property (nonatomic, copy) NSString *name;

@end


@implementation FSTask

@synthesize name = _name;

+ (FSTask *)taskWithName:(NSString *)name
{
    FSTask *task = [[FSTask alloc] init];
    if (task)
    {
        task.name = name;
    }
    return task;
}

- (void)performTaskWithCompletionBlock:(void (^)(NSString *taskResult))block
{
    NSString *message = [NSString stringWithFormat:@"%@: doing work on main thread ...", _name];

    NSLog(@"%@", message);

    if (block)
    {
        NSString *result = [NSString stringWithFormat:@"%@: result", _name];
        block(result);
    }
}

@end

FSAsyncTask.m - 后台线程的异步工作......

@interface FSAsyncTask ()

@property (nonatomic, copy) void (^block)(NSString *taskResult);
@property (nonatomic, copy) NSString *name;

- (void)performAsyncTask;

@end



@implementation FSAsyncTask

@synthesize block = _block;
@synthesize name  = _name;

+ (FSAsyncTask *)asyncTaskWithName:(NSString *)name
{
    FSAsyncTask *task = [[FSAsyncTask alloc] init];
    if (task)
    {
        task.name = name;
    }
    return task;
}

- (void)performTaskWithCompletionBlock:(void (^)(NSString *taskResult))block
{
    self.block = block;


    // the call below could be e.g. a NSURLConnection that's being opened,
    //  in this case a NSURLConnectionDelegate method will return the result
    //  in this delegate method the completion block could be called ...

    dispatch_queue_t queue = dispatch_queue_create("com.example.asynctask", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^ {

        [self performAsyncTask];

    });
}

#pragma mark - Private

- (void)performAsyncTask
{
    for (int i = 0; i < 5; i++)
    {
        NSString *message = [NSString stringWithFormat:@"%d - %@: doing work on background thread ...", i, _name];
        NSLog(@"%@", message);

        [NSThread sleepForTimeInterval:1];
    }


    // this completion block might be called from your delegate methods ...

    if (_block)
    {
        dispatch_async(dispatch_get_main_queue(), ^ {

            NSString *result = [NSString stringWithFormat:@"%@: result", _name];
            _block(result);

        });
    }
}

@end

答案 2 :(得分:1)

您可以将块属性分配给B,在调用委托方法之前,它将用于执行代码块。类似的东西:

@property(nonatomic,copy)void(^ yourBlock)(id blockParameter);

因此,在调用B的委托后,您可以调用此块并执行它。在这个区块内,你可以调用C的方法。

答案 3 :(得分:1)

我处理这个的方式是。

我在异步调用之前创建了一个NSMutableDictionary。

然后我进行异步调用。并检查我正在等待的价值

NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[AsyncCallClass asyncCall:^{
    @synchronized(dictionary) {
        [dictionary setValue:myValue forKey:@"result"];
    }
}];

while (true){
    @synchronized(dictionary){
        if ([dictionary valueForKey:@"resultValue"] != nil){
            break;
        }
    }
    [NSThread sleepForTimeInterval:.25];
}
MyResultClass *result = [dictionary valueForKey:@"resultValue"];

你可以为此添加时间以阻止它成为无限循环。但这是我的解决方案。它看起来效果很好。

答案 4 :(得分:1)

以下是我用来执行此类操作的典型代码(根据您的需要调整completionBlock签名和方法名称)

typedef void (^BCompletionBlock)(void);

@interface B : NSObject <BDelegate>
@property(nonatomic, copy) BCompletionBlock completionBlock;
-(void)doAsynchronousActionWithCompletion:(BCompletionBlock)aCompletionBlock;
@end

@implementation B
-(void)doAsynchronousActionWithCompletion:(BCompletionBlock)aCompletionBlock
{
    // Store the completion block for later use
    self.completionBlock = aCompletionBlock;
    // Then execute your asynchronous action, that will call some delegate method when done
    [self doYourAsynchronousActionWithDelegate:self];
}

-(void)yourBDelegateMethodCalledWhenDone
{
    // Upon your async task completion, call your completion block then
    if (self.completionBlock) self.completionBlock();
}
@end

然后是一个示例用法:

-(void)doActions
{
    [a doSynchronousAction];
    [b doAsynchronousActionWithCompletion:^{
       [c doSynchronousAction];
       // A,B,C are now done
    }];
}

我一直这样做“转换”使用委托方法的动作(告诉我它们什么时候完成)到使用completionBlocks的动作(有一些类为UIAlertViews,UIActionsSheets和更多的案例执行此操作)例如)它就像一个魅力。

在这种情况下,我发现使用completionBlocks要比委托机制容易得多。

答案 5 :(得分:0)

您也可以像这样在一个块中传递C ...

定义自定义块

typedef void(^myCompletion)(BOOL complete);

创建B方法

-(void)performBWithCompletionBlock:(myCompletion)complete;
{
  // do your things

  [self.delegate delegateCallback];
  complete(YES);

}

然后创建BG /异步ABC

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // now we're on a BG queue to perform our async tasks

             [self performA];
             [self performBWithCompletionBlock:^(BOOL complete) {

                if (complete == YES)
                    [self performC];
             }];
        });

如果你想让C在主线程上

dispatch_async(dispatch_get_main_queue(), ^{
    [self performC];
});