如何在方法参数中创建自己的完成处理程序

26

我想为某个类创建完成处理程序,而不是触发类的主要代码并等待委托回调。 我已经阅读了苹果文档,他们似乎没有很好地解释如何直接实现这样的东西。

4个回答

34

如果是针对一个异步方法,你可以这样做

- (void)asynchronousTaskWithCompletion:(void (^)(void))completion;
{
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Some long running task you want on another thread

    dispatch_async(dispatch_get_main_queue(), ^{
      if (completion) {
        completion();
      }
    });
  });
}

这将会被调用

[self asynchronousTaskWithCompletion:^{  NSLog(@"It finished");}];

需要注意的是要进行保护以确保completion指向某个值,否则如果尝试执行它,程序会崩溃。

我经常使用块来作为完成处理程序的方式之一,当一个视图控制器已经完成并希望从导航栈中弹出时。

@interface MyViewController : UIViewController

@property (nonatomic, copy) void (^onCompletion)(void);

@end

@implementation MyViewController

- (IBAction)doneTapped;
{
  if (self.onCompletion) {
    self.onCompletion();
  }
}

@end
您需要在将此视图推送到堆栈时设置完成块。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
{
  MyViewController *myViewController = segue.destinationViewController;
  myViewController.onCompletion = ^{
    [self.navigationController popViewControllerAnimated:YES];
  };
}

第二个异步调度调用不会在长时间运行的任务完成之前触发吗? - marciokoko
如果长时间运行的代码尚未完成,则您将无法到达下一个dispatch_async - Paul.s
整个代码块将被发送到另一个队列,但代码仍按顺序执行,因此在长时间运行的任务中发生的代码完成后,才会发生对 dispatch_async 的第二次调用。 - Paul.s
我认为这就是GCD队列的整个理念吧?让多个队列同时运行。我有一段代码必须使用第二个作为dispatch_sync,否则返回值为nil。这与串行和并行队列有关吗? - marciokoko
你最好的选择是创建一个带有你具体需求的问题 - 在这些评论中很难传达更多信息。 - Paul.s

34

您需要像对待变量一样处理完成块。该方法将接受块作为其参数的一部分,然后将其存储以备后用。

- (void)myMethodWithCompletionHandler:(void (^)(id, NSError*))handler;

您可以为该块类型进行typedef以便更容易阅读:

typedef void (^CompletionBlock)(id, NSError*);

然后将您的块存储为实例变量:

在您的@interface中:CompletionBlock _block;

在myMethod..中:_block = [handler copy]

然后当您想要执行完成块时,只需像常规块一样调用它:

_block(myData, error);


1
需要使用 copy 关键字将代码块复制到堆上(默认情况下,客户端传递给您的代码块存储在栈上)。 - yonel

4
这里有一个关于使用字符串和完成处理程序作为变量的方法的示例。完成处理程序也可以接收字符串。 Swift 2.2语法 定义:
func doSomething(input: String, completion: (result: String) -> Void {
    print(input)
    completion(result: "we are done!")
}

调用函数:

doSomething("cool put string!") { (result) in
        print(result)
}

0

Chris C的答案是正确的(对我非常有帮助),但有一个注意点:

@interface中放置声明CompletionBlock _block;不是线程安全的。

如果存在多个线程(或调度队列)调用myMethod…的可能性,请在myMethod…中放置CompletionBlock _block = [handler copy];

感谢@Chris C。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接