Skip to content
剑川道长 edited this page Nov 1, 2018 · 19 revisions

1.MDTask

2. MDTaskGroup

3. MDTaskList

4. MDTask与NSArray

5. MDTask与NSDictionary

6. MDTask与NSSet

MDTask 是一款任务管理工具。可以对任务进行并发、顺序执行。并且任意一个任务(组)也可以作为一个任务,与其他任务(组)重新组成并发、顺序 任务组,从而达到分离代码的目的。

1. MDTask

1.1 使用示例

创建任务

//代码见测试用例MDTask/TMDTask.m
- (void)setUp {
    [super setUp];
//...
    self.task1 = [MDTask task:^(MDTask *task, MDTaskFinish finish) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"t1 finish");
            finish(task, YES);
        });
    }];
//...
}

执行任务

//代码见测试用例MDTask/TMDTask.m
- (void)testTask {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
//...
    [self.task1 runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        [expectation fulfill];
    }];
//...
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

任务被取消

//目前只有在任务组中,某一个任务失败后,其他同级任务才会被取消。因此任务被取消的情况,会在后续任务组的介绍中举例。

任务失败重试

//代码见测试用例MDTask/TMDTaskRetry.m
- (void)testRetryTask {
//...
    MDTask *task1 = [MDTask task:^(MDTask *task, MDTaskFinish finish) {
        //...
    }
                   cancelBlock:NULL
                 taskFailBlock:^(MDTask *task, NSUInteger tryCount, void (^retry)(BOOL retry)) {
                     if (tryCount > 1) {
                         retry(NO);
                     } else {
                         retry(YES);
                     }
                 }];
//...
}

1.2 API详细介绍

//创建task
+ (MDTask *)task:(MDTaskBlock)taskBlock;
+ (MDTask *)task:(MDTaskBlock)taskBlock cancelBlock:(MDTaskCancelBlock)cancel;
+ (MDTask *)task:(MDTaskBlock)taskBlock cancelBlock:(MDTaskCancelBlock)cancel taskFailBlock:(MDTaskFailBlock)fail;

//执行task
- (BOOL)runWithFinish:(MDTaskFinish)finish;

taskBlock

/*
 * 任务执行体,在这个block中写具体的任务代码,如网络接口调用、图像处理、播放背景音乐等任务。
 * 在这个block中,需要在任务结束时手动调用finishBlock,并且传入task和结束原因(成功或者失败,为BOOL值)。
 */
typedef void (^MDTaskBlock)(MDTask *task, MDTaskFinish finish);

finishBlock

/*
 * finish block 是任务结束时的回调方法,也是任务执行体中需要用户手动调用的方法。
 * task为当前任务,succeed为任务结束原因(成功或者失败)
 */
typedef void (^MDTaskFinish)(__kindof MDTask *task, BOOL succeed);

failBlock

/*
 * 当任务执行体中,用户调用finish(task, NO)时,会导致当前任务失败,此时还不会调用最后的finish(task, NO)方法,而会首先回调failBlock;
 * 此回调会传入当前task、已重试次数、和重试方法retry;
 * 调用retry(YES)会重试此任务,调用retry(NO)终止任务,此时任务才会真正失败,进入到finish方法。
 */
typedef void (^MDTaskFailBlock)(MDTask *task, NSUInteger tryCount, void (^retry)(BOOL retry));

cancelBlock

/*
 * 此任务执行过程中,由于某些原因被取消时,会回调cancelBlock。取消原因往往是任务组中有些任务失败,导致任务组已经失败,从而任务组中所有任务都无需继续执行。
 * 在这个block中写一些此任务的清理代码,如取消网络接口调用、终止图像处理、结束背景音乐播放等。
 * 此方法在任务组合中比较有用。
 */
typedef void (^MDTaskCancelBlock)(MDTask *task);

2. MDTaskGroup

MDTaskGroup是一组task的无序集合,运行没有先后顺序之分,如果子任务是异步执行完成的,那么所有task将会并发执行。

如下面Demo中的task1和task2,都是用GCD延迟了1秒后finish,taskGroup并发组合了task1和task2,那么这个taskGroup也将会在1秒后执行完毕。

2.1 使用示例

创建、执行任务

//代码见测试用例MDTask/TMDTask.m


- (void)setUp {
    [super setUp];
//...
    self.task1 = [MDTask task:^(MDTask *task, MDTaskFinish finish) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"t1 finish");
            finish(task, YES);
        });
    }];
    
    self.task2 = [MDTask task:^(MDTask *task, MDTaskFinish finish) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"t2 finish");
            finish(task, YES);
        });
    }];
//...
}

- (void)testTaskGroup {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
    
    MDTaskGroup *tg = [MDTaskGroup taskGroup];
    [tg addTask:self.task1];
    [tg addTask:self.task2];
    /*
     * 也可以写成:
     * MDTaskGroup *tg = [MDTaskGroup taskGroupWithTasks:self.task1, self.task2, nil];
     * 这里最后的nil不能少
     */
    [tg runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        [expectation fulfill];
    }];
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

2.2 API详细介绍

创建一个taskGroup

//创建一个空的taskGroup
+ (MDTaskGroup *)taskGroup;
//创建一个空的taskGroup,并在其中添加一些子任务
+ (MDTaskGroup *)taskGroupWithTasks:(MDTask *)task, ...;

为taskGroup添加子任务

//使用taskBlock创建子task,然后添加到taskGroup中
- (BOOL)addTaskBlock:(MDTaskBlock)taskBlock;
//task可以是 MDTask、MDTaskGroup、MDTaskList
- (BOOL)addTask:(__kindof MDTask *)task;

移除所有task

/*
 * 如果调用此方法时,task正在运行,那么会等到运行结束时,移除所有子任务
 */
- (void)removeTasks;

2.3 注意事项

  1. 如果执行一个空的taskGroup,将会直接回调finish方法,并且结束状态是成功
  2. 如果taskGroup中某个子任务,经过重试之后,仍然失败,会触发这个taskGroup会失败
  3. 如果taskGroup中某个子任务,经过重试之后,仍然失败,会调用这个taskGroup中所有子任务调用cancelBlock

3. MDTaskList

MDTaskList是一组task的顺序集合,运行顺序为先添加的先执行,执行完毕,且结束状态为成功,才会继续执行下一个子任务。

如下面Demo中的task1和task2,都是用GCD延迟了1秒后finish,taskList顺序组合了task1和task2,那么将会首先执行task1,1秒后task1成功后,才开始执行task2,因此这个taskList将会在2秒后执行完毕。

3.1 使用示例

创建、执行任务

//代码见测试用例MDTask/TMDTask.m


- (void)setUp {
    [super setUp];
//...
    self.task1 = [MDTask task:^(MDTask *task, MDTaskFinish finish) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"t1 finish");
            finish(task, YES);
        });
    }];
    
    self.task2 = [MDTask task:^(MDTask *task, MDTaskFinish finish) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"t2 finish");
            finish(task, YES);
        });
    }];
//...
}

- (void)testTaskList {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
    
    MDTaskList *tl = [MDTaskList taskList];
    [tl addTask:self.task1];
    [tl addTask:self.task2];
    [tl runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        [expectation fulfill];
    }];
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

3.2 API详细介绍

创建一个taskList

//创建一个空的taskGroup
+ (MDTaskList *)taskList;
//创建一个空的taskList,并在其中添加一些子任务
+ (MDTaskList *)taskListWithTasks:(MDTask *)task, ...;

为taskList添加子任务

//使用taskBlock创建子task,然后添加到taskGroup中
- (BOOL)addTaskBlock:(MDTaskBlock)taskBlock;
//task可以是 MDTask、MDTaskGroup、MDTaskList
- (BOOL)addTask:(__kindof MDTask *)task;

移除所有task

/*
 * 如果调用此方法时,task正在运行,那么会等到运行结束时,移除所有子任务
 */
- (void)removeTasks;

3.3 注意事项

  1. 如果执行一个空的taskList,将会直接回调finish方法,并且结束状态是成功
  2. 如果taskList中某个子任务,经过重试之后,仍然失败,会触发这个taskList会失败
  3. 如果taskList中某个子任务,经过重试之后,仍然失败,会调用这个taskList中所有子任务调用cancelBlock

4. MDTask与NSArray

MDTask与NSArray相关代码,使用NSArray的分类完成 此分类提供了两个方法,可以将NSArray的每个元素转成子任务,并且将子任务合并成taskGroup或者taskList,taskList的执行顺序和NSArray的顺序相同。

4.1 使用示例

NSArray转MDTaskGroup:

//代码见测试用例MDTask/TMDTasks.m

- (void)testArrayGroup {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
    NSArray *array = @[
                       @"0",
                       @"1",
                       @"2",
                       @"3",
                       @"4",
                       @"5",
                       @"6",
                       @"7",
                       @"8",
                       @"9",
                       ];
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:array.count];
    MDTaskGroup *tg = [array lt_taskGroupWithObjectTask:^(MDTask * _Nonnull task, MDTaskFinish  _Nonnull finish, NSString *obj, NSUInteger idx) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            NSLog(@"task: %@", obj);
            [result addObject:[obj stringByAppendingString:[@(idx) stringValue]]];
            dispatch_async(dispatch_get_main_queue(), ^{
                finish(task, YES);
            });
        });
    }];
    
    [tg runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        NSLog(@"%@", result);
        [expectation fulfill];
    }];
    
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

NSArray转MDTaskList:

//代码见测试用例MDTask/TMDTasks.m
- (void)testArrayList {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
    NSArray *array = @[
                       @"0",
                       @"1",
                       @"2",
                       @"3",
                       @"4",
                       @"5",
                       @"6",
                       @"7",
                       @"8",
                       @"9",
                       ];
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:array.count];
    MDTaskList *tg = [array lt_taskListWithObjectTask:^(MDTask * _Nonnull task, MDTaskFinish  _Nonnull finish, NSString *obj, NSUInteger idx) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            NSLog(@"task: %@", obj);
            [result addObject:[obj stringByAppendingString:[@(idx) stringValue]]];
            dispatch_async(dispatch_get_main_queue(), ^{
                finish(task, YES);
            });
        });
    }];
    
    [tg runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        NSLog(@"%@", result);
        [expectation fulfill];
    }];
    
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

4.2 API详细介绍

MArrayObjectTaskBlock

typedef void(^MArrayObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull obj, NSUInteger idx);

MArrayObjectTaskBlock由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 obj:当前元素 idx:当前元素的索引

数组转taskGroup、taskList

- (MDTaskGroup *_Nullable)mdt_taskGroupWithObjectTask:(MArrayObjectTaskBlock _Nonnull)objectTask;
- (MDTaskList *_Nonnull)mdt_taskListWithObjectTask:(MArrayObjectTaskBlock _Nonnull)objectTask;

5. MDTask与NSDictionary

MDTask与NSDictionary相关代码,使用NSDictionary的分类完成 此分类提供了两个方法,可以将NSDictionary的每个元素转成子任务,并且将子任务合并成taskGroup或者taskList,taskList的执行顺序和[NSDictionary allKeys]的顺序相同。

5.1 使用示例

NSDictionary转MDTaskGroup:

//代码见测试用例MDTask/TMDTasks.m

- (void)testDictionaryGroup {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
    NSDictionary *dictionary = @{
                                 @"0": @"_0",
                                 @"1": @"_1",
                                 @"2": @"_2",
                                 @"3": @"_3",
                                 @"4": @"_4",
                                 @"5": @"_5",
                                 @"6": @"_6",
                                 @"7": @"_7",
                                 @"8": @"_8",
                                 @"9": @"_9",
                                 };
    NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:dictionary.count];
    MDTaskGroup *tg = [dictionary lt_taskGroupWithObjectTask:^(MDTask * _Nonnull task, MDTaskFinish  _Nonnull finish, NSString * _Nonnull key, NSString *  _Nonnull obj) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            result[key] = [key stringByAppendingString:obj];
            dispatch_async(dispatch_get_main_queue(), ^{
                finish(task, YES);
            });
        });
    }];
    
    [tg runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        NSLog(@"%@", result);
        [expectation fulfill];
    }];
    
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

NSDictionary转MDTaskList:

//代码见测试用例MDTask/TMDTasks.m
- (void)testDictionaryList {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
    NSDictionary *dictionary = @{
                                 @"0": @"_0",
                                 @"1": @"_1",
                                 @"2": @"_2",
                                 @"3": @"_3",
                                 @"4": @"_4",
                                 @"5": @"_5",
                                 @"6": @"_6",
                                 @"7": @"_7",
                                 @"8": @"_8",
                                 @"9": @"_9",
                                 };
    NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:dictionary.count];
    MDTaskList *tg = [dictionary lt_taskListWithObjectTask:^(MDTask * _Nonnull task, MDTaskFinish  _Nonnull finish, NSString * _Nonnull key, NSString *  _Nonnull obj, NSUInteger idx) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            result[key] = [key stringByAppendingString:obj];
            dispatch_async(dispatch_get_main_queue(), ^{
                finish(task, YES);
            });
        });
    }];
    
    [tg runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        NSLog(@"%@", result);
        [expectation fulfill];
    }];
    
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

5.2 API详细介绍

MDictionaryKeyObjectTaskBlock

typedef void(^MDictionaryKeyObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull key, id _Nonnull obj);

由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 key: 当前元素的key obj:当前元素

MDictionaryIdxKeyObjectTaskBlock

typedef void(^MDictionaryIdxKeyObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull key, id _Nonnull obj, NSUInteger idx);

由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 key: 当前元素的key obj:当前元素 idx:当前元素的key在[NSDictionary allKeys]数组中的索引

字典转taskGroup、taskList

- (MDTaskGroup *_Nullable)mdt_taskGroupWithObjectTask:(MDictionaryKeyObjectTaskBlock _Nonnull)objectTask;
- (MDTaskList *_Nonnull)mdt_taskListWithObjectTask:(MDictionaryIdxKeyObjectTaskBlock _Nonnull)objectTask;

6. MDTask与NSSet

MDTask与NSSet相关代码,使用NSSet的分类完成 此分类提供了两个方法,可以将NSSet的每个元素转成子任务,并且将子任务合并成taskGroup或者taskList,taskList的执行顺序和[NSSet allObjects]的顺序相同。

6.1 使用示例

NSDictionary转MDTaskGroup:

//代码见测试用例MDTask/TMDTasks.m
- (void)testSetGroup {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
    NSSet *set = [NSSet setWithObjects:
                  @"0",
                  @"1",
                  @"2",
                  @"3",
                  @"4",
                  @"5",
                  @"6",
                  @"7",
                  @"8",
                  @"9", nil];
    NSMutableSet *result = [NSMutableSet setWithCapacity:set.count];
    
    MDTaskGroup *tg = [set md_taskGroupWithObjectTask:^(MDTask * _Nonnull task, MDTaskFinish  _Nonnull finish, NSString * _Nonnull obj) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            [result addObject:[obj stringByAppendingString:@"hello"]];
            dispatch_async(dispatch_get_main_queue(), ^{
                finish(task, YES);
            });
        });
    }];
    
    [tg runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        NSLog(@"%@", result);
        [expectation fulfill];
    }];
    
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

NSDictionary转MDTaskList:

//代码见测试用例MDTask/TMDTasks.m
- (void)testSetList {
    XCTestExpectation *expectation = [self expectationWithDescription:@""];
    NSSet *set = [NSSet setWithObjects:
                  @"0",
                  @"1",
                  @"2",
                  @"3",
                  @"4",
                  @"5",
                  @"6",
                  @"7",
                  @"8",
                  @"9", nil];
    NSMutableSet *result = [NSMutableSet setWithCapacity:set.count];
    MDTaskList *tl = [set md_taskListWithObjectTask:^(MDTask * _Nonnull task, MDTaskFinish  _Nonnull finish, id  _Nonnull obj, NSUInteger idx) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            [result addObject:[obj stringByAppendingString:[@(idx) stringValue]]];
            dispatch_async(dispatch_get_main_queue(), ^{
                finish(task, YES);
            });
        });
    }];
    [tl runWithFinish:^(__kindof MDTask *task, BOOL succeed) {
        NSLog(@"%@", result);
        [expectation fulfill];
    }];
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

6.2 API详细介绍

MSetObjectTaskBlock

typedef void(^MSetObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull obj);

由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 obj:当前元素

MSetIdxObjectTaskBlock

typedef void(^MSetIdxObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull obj, NSUInteger idx);

由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 obj:当前元素 idx:当前元素的key在[NSSet allObjects]数组中的索引

集合转taskGroup、taskList

- (MDTaskGroup *_Nullable)mdt_taskGroupWithObjectTask:(MSetObjectTaskBlock _Nonnull)objectTask;
- (MDTaskList *_Nonnull)mdt_taskListWithObjectTask:(MSetIdxObjectTaskBlock _Nonnull)objectTask;

Clone this wiki locally