ios - 使用 Box sdk 的异步递归调用 - 如何使用 iOS BOX sdk api 迭代所有文件

标签 ios objective-c recursion box-api

我正在尝试迭代存储在 Box 帐户中的所有图像。

以下代码不正确,似乎找不到错误。

问题似乎是在等待所有异步递归调用完成,以便知道何时没有更多图像可获取以发出完成信号

-(void)enumerateFiles
{
    [self recursiveEnumerateFilesAtPath:@"0" completion:nil];
}


-(void)recursiveEnumerateFilesAtPath:(NSString *)folderID completion:(void(^)())block
{
    static NSArray *imageExtensions;
    imageExtensions = @[@"jpg",@"jpeg",@"png"];
    [self enumerateFilesAtPath:folderID completion:^(BoxCollection *collection) {
        NSUInteger numberOfItems = collection.numberOfEntries;
        for (int i = 0; i < numberOfItems; i++) {
            id model = [collection modelAtIndex:i];

            if ([model isKindOfClass:[BoxFolder class]])
            {
                BoxFolder *folder = (BoxFolder *)model;
                dispatch_group_t group = dispatch_group_create();
                dispatch_group_enter(group);
                [self recursiveEnumerateFilesAtPath:folder.modelID completion:^{
                    dispatch_group_leave(group);
                }];
                dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
            } else if ([model isKindOfClass:[BoxItem class]])
            {
                BoxItem *item = (BoxItem *)model;
                NSString *extension = [[item.name pathExtension] lowercaseString];
                if ([imageExtensions containsObject:extension])
                {
                    [self.items addObject:model];
                }

            }
        }
        if (block)
        {
            block();
        }
    }];
}
-(void)enumerateFilesAtPath:(NSString *)folderID completion:(void(^)(BoxCollection *collection))block
{
    BoxCollectionBlock success = ^(BoxCollection *collection)
    {
        block(collection);
    };

    BoxAPIJSONFailureBlock failure = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSDictionary *JSONDictionary)
    {
        block(nil);
    };

    [[BoxSDK sharedSDK].foldersManager folderItemsWithID:folderID requestBuilder:nil success:success failure:failure];
}

最佳答案

可行方法的关键是制作内循环,即

for (int i = 0; i < numberOfItems; i++) {..}

顺序调用多个异步任务并且本身是异步的。异步任务应该是一个 block ( ^ ),它将调用外部方法并捕获状态,以便其外部方法 recursiveEnumerateFilesAtPath:可以以递归方式调用。

请注意,这不是我们从函数中了解到的递归,其中函数返回之前调用自身,并且状态保存在程序堆栈中。在这里,我们宁愿有一个迭代(异步循环)——堆栈上没有状态,相反,“递归”所需的状态保存在 block 中捕获的变量中,这些变量驻留在堆。

也就是说,您需要将“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 类别 forEachApplyTask:completion:”。

现在,假设有一个 NSArray 的类别使用异步 forEachApplyTask:completion:方法:

@interface NSArray (AsyncExtension)
- (void) forEachApplyTask:(unary_async_t)task completion:(completion_t) completion;
@end

此方法采用类型为 unary_async_t 的 block 作为第一个参数:

typedef void (^unary_async_t)(id input, completion_t completion);

和一个完成处理程序作为第二个参数:

typedef void (^completion_t)(id result);

这个完成处理程序的 typedef 是通用的,可用于所有完成处理程序变体。

假设 block 任务 将按顺序应用于接收器(数组)中的每个元素。假设这个数组是原始代码中的“模型”数组——您只需要从 BoxCollection 中创建一个“模型”数组。对象。

完成后,完成处理程序会传递一个结果,该结果是一个数组,其中包含按相同顺序排列的每个任务的结果。

此任务执行您之前的 {..} 中的 block ( 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");
            }
        }];
    }];
}

注意:未经测试,您可能会遇到问题!

请注意,一切都是异步运行的,并且没有将状态放在堆栈上的递归。递归迭代文件夹所需的状态放在堆上,包含在 block 中捕获的变量中。


NSArray 分类方法 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

关于ios - 使用 Box sdk 的异步递归调用 - 如何使用 iOS BOX sdk api 迭代所有文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20495920/

相关文章:

objective-c - 如何访问父 View ?

javascript - 为什么这个内存实现对匿名函数有效,但对声明的函数无效?

objective-c - 将 NSString 与常量进行比较 #define string : isEqualToString or ==?

iOS加载动态图层蒙版(即图层蒙版在代码外提供)

ios - 从模块 'RealmSwift' 读取时遇到错误

ios - 我如何强制 UILabel 比应有的宽 5 点?

objective-c - Objective C类之间的通信

java - Arraylist 没有正确地递归更新

haskell - 下面的函数尾调用优化了吗?

ios - Swift 中的 Facebook 登录管理器 "expression is ambiguous"错误