-
Notifications
You must be signed in to change notification settings - Fork 1
MDTask
MDTask 是一款任务管理工具。可以对任务进行并发、顺序执行。并且任意一个任务(组)也可以作为一个任务,与其他任务(组)重新组成并发、顺序 任务组,从而达到分离代码的目的。
//代码见测试用例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);
}
}];
//...
}//创建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;/*
* 任务执行体,在这个block中写具体的任务代码,如网络接口调用、图像处理、播放背景音乐等任务。
* 在这个block中,需要在任务结束时手动调用finishBlock,并且传入task和结束原因(成功或者失败,为BOOL值)。
*/
typedef void (^MDTaskBlock)(MDTask *task, MDTaskFinish finish);/*
* finish block 是任务结束时的回调方法,也是任务执行体中需要用户手动调用的方法。
* task为当前任务,succeed为任务结束原因(成功或者失败)
*/
typedef void (^MDTaskFinish)(__kindof MDTask *task, BOOL succeed);/*
* 当任务执行体中,用户调用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。取消原因往往是任务组中有些任务失败,导致任务组已经失败,从而任务组中所有任务都无需继续执行。
* 在这个block中写一些此任务的清理代码,如取消网络接口调用、终止图像处理、结束背景音乐播放等。
* 此方法在任务组合中比较有用。
*/
typedef void (^MDTaskCancelBlock)(MDTask *task);MDTaskGroup是一组task的无序集合,运行没有先后顺序之分,如果子任务是异步执行完成的,那么所有task将会并发执行。
如下面Demo中的task1和task2,都是用GCD延迟了1秒后finish,taskGroup并发组合了task1和task2,那么这个taskGroup也将会在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];
}
//创建一个空的taskGroup
+ (MDTaskGroup *)taskGroup;
//创建一个空的taskGroup,并在其中添加一些子任务
+ (MDTaskGroup *)taskGroupWithTasks:(MDTask *)task, ...;//使用taskBlock创建子task,然后添加到taskGroup中
- (BOOL)addTaskBlock:(MDTaskBlock)taskBlock;
//task可以是 MDTask、MDTaskGroup、MDTaskList
- (BOOL)addTask:(__kindof MDTask *)task;/*
* 如果调用此方法时,task正在运行,那么会等到运行结束时,移除所有子任务
*/
- (void)removeTasks;- 如果执行一个空的taskGroup,将会直接回调finish方法,并且结束状态是成功
- 如果taskGroup中某个子任务,经过重试之后,仍然失败,会触发这个taskGroup会失败
- 如果taskGroup中某个子任务,经过重试之后,仍然失败,会调用这个taskGroup中所有子任务调用cancelBlock
MDTaskList是一组task的顺序集合,运行顺序为先添加的先执行,执行完毕,且结束状态为成功,才会继续执行下一个子任务。
如下面Demo中的task1和task2,都是用GCD延迟了1秒后finish,taskList顺序组合了task1和task2,那么将会首先执行task1,1秒后task1成功后,才开始执行task2,因此这个taskList将会在2秒后执行完毕。
//代码见测试用例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];
}
//创建一个空的taskGroup
+ (MDTaskList *)taskList;
//创建一个空的taskList,并在其中添加一些子任务
+ (MDTaskList *)taskListWithTasks:(MDTask *)task, ...;//使用taskBlock创建子task,然后添加到taskGroup中
- (BOOL)addTaskBlock:(MDTaskBlock)taskBlock;
//task可以是 MDTask、MDTaskGroup、MDTaskList
- (BOOL)addTask:(__kindof MDTask *)task;/*
* 如果调用此方法时,task正在运行,那么会等到运行结束时,移除所有子任务
*/
- (void)removeTasks;- 如果执行一个空的taskList,将会直接回调finish方法,并且结束状态是成功
- 如果taskList中某个子任务,经过重试之后,仍然失败,会触发这个taskList会失败
- 如果taskList中某个子任务,经过重试之后,仍然失败,会调用这个taskList中所有子任务调用cancelBlock
MDTask与NSArray相关代码,使用NSArray的分类完成 此分类提供了两个方法,可以将NSArray的每个元素转成子任务,并且将子任务合并成taskGroup或者taskList,taskList的执行顺序和NSArray的顺序相同。
//代码见测试用例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];
}//代码见测试用例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];
}typedef void(^MArrayObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull obj, NSUInteger idx);MArrayObjectTaskBlock由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 obj:当前元素 idx:当前元素的索引
- (MDTaskGroup *_Nullable)mdt_taskGroupWithObjectTask:(MArrayObjectTaskBlock _Nonnull)objectTask;
- (MDTaskList *_Nonnull)mdt_taskListWithObjectTask:(MArrayObjectTaskBlock _Nonnull)objectTask;MDTask与NSDictionary相关代码,使用NSDictionary的分类完成 此分类提供了两个方法,可以将NSDictionary的每个元素转成子任务,并且将子任务合并成taskGroup或者taskList,taskList的执行顺序和[NSDictionary allKeys]的顺序相同。
//代码见测试用例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];
}//代码见测试用例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];
}typedef void(^MDictionaryKeyObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull key, id _Nonnull obj);由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 key: 当前元素的key obj:当前元素
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]数组中的索引
- (MDTaskGroup *_Nullable)mdt_taskGroupWithObjectTask:(MDictionaryKeyObjectTaskBlock _Nonnull)objectTask;
- (MDTaskList *_Nonnull)mdt_taskListWithObjectTask:(MDictionaryIdxKeyObjectTaskBlock _Nonnull)objectTask;MDTask与NSSet相关代码,使用NSSet的分类完成 此分类提供了两个方法,可以将NSSet的每个元素转成子任务,并且将子任务合并成taskGroup或者taskList,taskList的执行顺序和[NSSet allObjects]的顺序相同。
//代码见测试用例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];
}//代码见测试用例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];
}typedef void(^MSetObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull obj);由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 obj:当前元素
typedef void(^MSetIdxObjectTaskBlock)(MDTask * _Nonnull task, MDTaskFinish _Nonnull finish, id _Nonnull obj, NSUInteger idx);由使用者提供,此block会针对数组中的每个元素进行回调,使用者需要在这里写针对每个元素的处理任务 task:当前子任务 finish:当前子任务的finishBlock,需要用户在处理完当前元素后手动调用 obj:当前元素 idx:当前元素的key在[NSSet allObjects]数组中的索引
- (MDTaskGroup *_Nullable)mdt_taskGroupWithObjectTask:(MSetObjectTaskBlock _Nonnull)objectTask;
- (MDTaskList *_Nonnull)mdt_taskListWithObjectTask:(MSetIdxObjectTaskBlock _Nonnull)objectTask;