迭代并调用块(在NSMutableArray中)

时间:2015-03-10 16:33:04

标签: objective-c objective-c-blocks

我想将块添加到NSMutableArray,并遍历数组并调用块。

我有:

-(void)doSomethingAsyncWithCompletionHandler:(void(^)())completion {
  if (alreadyDoingSomething) {
     // Only add block, will be called after doSomethingAsync has finished
     [self.completionHandlers addObject: [completion copy]];
  } else {
     // Add block, then doSomethingAsync
     [self.completionHandlers addObject: [completion copy]];
     [self doSomethingAsync];
  }
}

使用示例:

[self doSomethingAsyncWithCompletionHandler:
   [self doStuff];
}];

[self doSomethingAsyncWithCompletionHandler:
   [self doMoreStuff];
}];

所以,鉴于以上是一种正确的方法(我不确定是否将数据块添加到数组中),doSomethingAsync中我想要的是:

-(void)doSomethingAsync {
   // Do stuff async, e.g. get application token.
   // When done:
   //for (block in self.completionhandlers) {
   //    //call block
   //    block();
   //}
}

如何迭代并调用块?

更新: 我要解决的具体问题是: 我有一个单独的LoginManager类,当用户或系统尝试执行需要身份验证的操作时,它会负责显示登录屏幕(UIAlertView)。理论上,系统可以调用需要身份验证的方法,然后在用户有机会完成登录之前调用另一个也需要身份验证的方法。

为了确保第一个和第二个动作最终都被调用,我尝试将它们作为块保存在一个数组中,这样一旦用户完成登录就可以调用它们。

我这样用:

-(void)syncRoutesInManagedObjectContext:(NSManagedObjectContext *)context onSuccess:(void(^)())successCompletionHandler onError:(void(^)())errorCompletionHandler {
    if (![[Settings applicationToken] length]) {
        [[LoginManager sharedManager] showLoginViewOnSuccess:^{
            [self syncRoutesInManagedObjectContext:context onSuccess:successCompletionHandler onError:errorCompletionHandler];
        }];
    } else if (![[Settings sessionToken] length]) {
        [[ARAPIConnectionManager sharedManager] getSessionTokenWithCompletionHandler:^{
            [self syncRoutesInManagedObjectContext:context onSuccess:successCompletionHandler onError:errorCompletionHandler];
        }];
    } else {
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@/routes.json", API_ROOT_URL, [Settings sessionToken]]];
        // Get routes
    }
}

上面的代码(syncRoutes ..)可能导致登录UIAlertView出现。然后,用户输入凭证并返回。当我尝试从服务器获取应用程序令牌时,UIAlertView被解雇。这意味着用户可以启动另一个方法,例如syncDestinations。在我有应用程序令牌并且登录过程完成之前,这个其他方法也无法做到这一点。因此,我将方法调用保存在一个块中并将该块添加到数组中,因此只要我有应用程序令牌就可以调用它。

希望现在我的问题更清楚了。保存积木等对我来说是一个新的领域,所以我不知道,即使它现在运作得很好,这是最好的方法。

2 个答案:

答案 0 :(得分:1)

不确定您要解决的问题,但您可以使用dispatch_queue将您的处理程序排入队列。根据您的问题,这可以是并发或串行队列。

最初,您将调度队列设置为挂起模式(dispatch_suspend())。您可以将块排队到队列中(使用dispatch_async(),直到队列恢复后才会执行。然后,任务的实际完成处理程序将恢复队列(dispatch_resume()),而队列将依次返回执行处理程序。

这是所有线程安全的,没有额外的同步。

<强>更新

// Create the initially suspended handler queue:
dispatch_queue_t handlerQueue = dispatch_create("handlerQueue", NULL);
dispatch_suspend(handlerQueue);

// add blocks:
dispatch_async(handlerQueue, ^{
    ...
});
dispatch_async(handlerQueue, ^{
    ...
});


// Start your asynchronous task whose completion handler 
// resumes the handler queue
[self taskWithCompletion:^{
    dispatch_resume(self.handlerQueue);
}];

这是一种非常简化的方法。当您想要将结果从任务的完成处理程序传递到在处理程序队列中排队的块,想要实现取消以及想要传递“异常”时,它会变得更具挑战性。

考虑到这些要求,使用“承诺”或“期货”也可以很好地解决您的问题。 (您可以在git hub上搜索RXPromise,这是Objective-C中承诺的一个实现(我是作者)。还有一些其他实现,还有更优雅的实现在斯威夫特。)

答案 1 :(得分:0)

想想我找到了。这似乎有效:

for (void (^completionHandler)() in self.completionHandlers) {
   completionHandler();
}
[self.completionHandlers removeAllObjects];

如果有人在泄漏方面对此代码有什么要说的话,请做!