5

长期潜伏者,第一次海报。我对Objective-C比较陌生,所以如果我问一些相当简单的问题,我很抱歉。我的 google & stack overflow-fu 让我失望了,所以我想有人可以帮忙。

我有一个同步进程连续执行三个函数 - 称之为 A -> B-> C ,其中任务 A 执行,然后是 B,然后是 C。

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

最初天真的解决方案是 -

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

...但这似乎真的很蹩脚。

我怀疑我可以用某种块来做到这一点,但对于我的生活,我就是想不通。有人可以帮忙吗?

感谢任何帮助!

纪尧姆

4

6 回答 6

5

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

首先,我将 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 的新手,所以我的方法可能存在明显的问题)。

于 2012-09-07T12:58:07.480 回答
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
于 2012-09-07T13:39:41.600 回答
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
    }];
}

我一直这样做是为了将使用委托方法的操作(告诉我它们何时完成)“转换”为使用完成块的操作(例如,有一些类可以为 UIAlertViews、UIActionsSheets 和更多案例执行此操作)和它就像一个魅力。

我发现在这种情况下使用完成块比使用委托机制更容易。

于 2012-09-07T13:14:01.453 回答
1

您可以将块属性分配给 B,在调用委托方法之前,它将用于执行代码块。就像是:

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

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

于 2012-08-29T18:21:23.117 回答
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"];

您也可以为此添加超时时间以阻止它成为无限循环。但这是我的解决方案。它似乎工作得很好。

于 2012-08-29T18:22:46.930 回答
0

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

定义一个自定义块

typedef void(^myCompletion)(BOOL complete);

创建你的 B 方法

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

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

}

然后创建 BG / async 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];
});
于 2014-11-10T18:35:09.573 回答