可行办法的关键是做内循环,也就是
for (int i = 0; i < numberOfItems; i++) {..}
顺序调用许多异步任务并且本身异步的构造。异步任务应该是一个块 (^),它将调用外部方法并捕获状态,以便可以以递归方式调用其外部方法 recursiveEnumerateFilesAtPath:。
请注意,这不是我们从函数中知道的递归,其中函数在返回之前调用自身,并且状态保存在程序堆栈中。在这里,我们宁愿有一个迭代(异步循环)——堆栈上没有状态,相反,“递归”所需的状态保存在块中捕获的变量中,这些变量驻留在堆。
也就是说,你需要将“for循环”(for (int i = 0; i < numberOfItems; i++) {..})转化为一些异步方法,例如:
- (void) forEach:(NSArray*)objects applyTask:(task_t) completion:(completion_t)completionHandler;
其中 task 是异步的,具有完成处理程序,并且(有条件地)调用外部方法 recursiveEnumerateFilesAtPath:。
这个forEach 构造可以使用NSOperationQueue 来实现,其最大并发操作数设置为1,其中任务是带有完成处理程序的NSOperation。或者 - 在这种情况下更简单,它可以使用调度队列和调度组来实现。或者 - 更简单的是,它可以使用“异步循环”来实现。我将展示一个可能的实现,请参阅下面的“NSArray Category forEachApplyTask:completion:”。
现在,假设 NSArray 有一个具有异步 forEachApplyTask:completion: 方法的类别:
@interface NSArray (AsyncExtension)
- (void) forEachApplyTask:(unary_async_t)task completion:(completion_t) completion;
@end
此方法将unary_async_t 类型的块作为第一个参数:
typedef void (^unary_async_t)(id input, completion_t completion);
和一个完成处理程序作为第二个参数:
typedef void (^completion_t)(id result);
完成处理程序的这种 typedef 是通用,可用于所有完成处理程序变体。
假设,块 task 将按顺序应用于接收器(数组)中的每个元素。假设,这个数组是原始代码中的一个“模型”数组——你只需要从BoxCollection 对象中创建一个“模型”数组。
完成后,完成处理程序传递一个结果,该结果是一个数组,其中包含每个任务的结果,顺序相同。
此任务执行您以前的 for loop 中的块 ({..})。
您的方法recursiveEnumerateFilesAtPath: 将不可避免地成为异步方法(因为在其实现中调用了异步方法),因此也获得了完成处理程序参数。
根据上面给出的假设,您可以实现如下所示的问题:
-(void)recursiveEnumerateFilesAtPath:(NSString *)folderID
completion:(completion_t)finalCompletionHandler
{
[self enumerateFilesAtPath:folderID completion:^(BoxCollection *collection) {
NSArray* models = ...; // create an NSArray from the collection
[models forEachApplyTask:^(id model, completion_t taskCompletionHandler) {
if ([model isKindOfClass:[BoxFolder class]]) {
BoxFolder *folder = (BoxFolder *)model;
[self recursiveEnumerateFilesAtPath:folder.modelID completion:^(id result){
// result should be @"finished folder"
taskCompletionHandler(@"folder"); // taskCompletionHandler will never be nil
}];
}
else if ([model isKindOfClass:[BoxItem class]]) {
BoxItem *item = (BoxItem *)model;
NSString *extension = [[item.name pathExtension] lowercaseString];
if ([imageExtensions containsObject:extension]) {
[self.items addObject:model];
}
taskCompletionHandler(@"item");
}
}
completion:^(id array){
// this folder is finished. array may be for example @[@"folder", @"item", @"item"]
if (finalCompletionHandler) {
finalCompletionHandler(@"finished folder");
}
}];
}];
}
注意:未经测试,您可能会遇到问题!
请注意,一切都是异步运行的,并且没有将其状态放入堆栈的递归。递归迭代文件夹所需的状态放在堆上,包含在块中捕获的变量中。
NSArray Category 方法forEachApplyTask:completion: 是一个可重用的组件,可以在很多情况下使用。可以按如下方式实现:
分类NSArray的实现
/**
Objective:
Asynchronously transform or process an array of items - one after the
other and return the result of each transform in an array.
Synopsis:
void transform_each(NSArray* inArray, unary_async_t task, completion_t completion);
*/
#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
/**
Typedef of a generic completion handler for an asynchronous task.
The parameter _result_ is the eventual result of the asynchronous
task. In case the task has been failed _result_ SHALL be an
NSError object.
*/
typedef void (^completion_t)(id result);
/**
Typedef for an asynchronous "transform" function. It's an
unary block taking an input as parameter and signals
the eventual result via a completion handler.
*/
typedef void (^unary_async_t)(id input, completion_t completion);
/**
`transform_each` sequentially applies an asynchronous transform function
to each object in the input array _inArray_ and signals the result as an
array containing the result of each transform applied to the input object.
Function `transform_each` is itself a asynchronous function, that is,
its eventual result will be signaled to the client through a completion
handler.
The result array contains the transformed objects in order of the
corresponding input array.
*/
void transform_each(NSArray* inArray, unary_async_t task, completion_t completion);
// implementation
static void do_each(NSEnumerator* iter, unary_async_t task, NSMutableArray* outArray, completion_t completion)
{
id obj = [iter nextObject];
if (obj == nil) {
if (completion)
completion([outArray copy]);
return;
}
task(obj, ^(id result){
[outArray addObject:result];
do_each(iter, task, outArray, completion);
});
}
void transform_each(NSArray* inArray, unary_async_t task, completion_t completion) {
NSMutableArray* outArray = [[NSMutableArray alloc] initWithCapacity:[inArray count]];
NSEnumerator* iter = [inArray objectEnumerator];
do_each(iter, task, outArray, completion);
}
/*******************************************************************************
Example
*******************************************************************************/
// A Category for NSArray
@interface NSArray (AsyncExtension)
- (void) async_forEachApplyTask:(unary_async_t) task completion:(completion_t) completion;
@end
@implementation NSArray (AsyncExtension)
- (void) async_forEachApplyTask:(unary_async_t) task completion:(completion_t) completion {
transform_each(self, task, completion);
}
@end
另请参阅:https://gist.github.com/couchdeveloper/6155227