Bolts
简介
自从Parse加盟Facebook后,他们发现很多细小的功能都分别在各自的sdk中实现了。于是他们决定开发一个更底层的库协调他们的sdk之间的工作。现在已经将这个库开源,这就是Bolts。
组件
task
第一个组件就是task,这是为了解决async callbacks而产生的。BFTask的相关源码并不多,主要是以BFTask, BFTaskCompletionSource, BFExector 这三个为主。
BFTask的使用非常简单,如果开发过程中有一个可以异步执行的方法,那么就可以执行这个方法并且返回一个BFTask指针,然后在continuation block中处理这个异步方法执行后的操作。
-
基本用法
// Objective-C - (BFTask *) fetchAsync:(PFObject *)object { / * * 创建一个标示BFTask是否完成的类,BFTaskCompletionSource本身就含有一个BFTask. * 在下面的代码中object完成fetchInBackgroundWithBlock操作后,对taskSource中的task进行设置,标示这个task的完成情况,用于外部对这个task的后续处理。 */ BFTaskCompletionSource *taskSource = [BFTaskCompletionSource taskCompletionSource]; [object fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) { if (!error) { [taskSource setResult:object]; } else { [taskSource setError:error]; } }]; return taskSource.task; } // Objective-C [[self fetchAsync:obj] continueWithBlock:^id(BFTask *task) { if (task.result) { // fetchAsync task 成功 } else if (task.error) { // fetchAsync task 失败 } return nil; }]; // Objective-C [[self fetchAsync:obj] continueWithSuccessBlock:^id(BFTask *task) { // 如果只需要关心成功情况可以使用continueWithSuccessBlock return nil; }];
-
链式用法
// Objective-C PFQuery *query = [PFQuery queryWithClassName:@"Student"]; [query orderByDescending:@"gpa"]; [[[[[self findAsync:query] continueWithSuccessBlock:^id(BFTask *task) { NSArray *students = task.result; PFObject *valedictorian = [students objectAtIndex:0]; [valedictorian setObject:@YES forKey:@"valedictorian"]; return [self saveAsync:valedictorian]; }] continueWithSuccessBlock:^id(BFTask *task) { PFObject *valedictorian = task.result; return [self findAsync:query]; }] continueWithSuccessBlock:^id(BFTask *task) { NSArray *students = task.result; PFObject *salutatorian = [students objectAtIndex:1]; [salutatorian setObject:@YES forKey:@"salutatorian"]; return [self saveAsync:salutatorian]; }] continueWithSuccessBlock:^id(BFTask *task) { // Everything is done! return nil; }];
-
串行任务
/ * * 通过PFQuery进行异步查询,然后将查询结果异步删除,并且删除操作是串行的. */ // Objective-C PFQuery *query = [PFQuery queryWithClassName:@"Comments"]; [query whereKey:@"post" equalTo:@123]; [[[self findAsync:query] continueWithBlock:^id(BFTask *task) { NSArray *results = task.result; // 创建一个开始的任务,之后的每一个deleteAsync操作都会依次在这个任务之后顺序进行. BFTask *task = [BFTask taskWithResult:nil]; for (PFObject *result in results) { // For each item, extend the task with a function to delete the item. task = [task continueWithBlock:^id(BFTask *task) { // Return a task that will be marked as completed when the delete is finished. return [self deleteAsync:result]; }]; } // 返回的是最后一个deleteAsync操作的task return task; }] continueWithBlock:^id(BFTask *task) { // Every comment was deleted. return nil; }];
-
并行任务
/ * * 通过PFQuery进行异步查询,然后将查询结果异步删除,并且删除操作是并行的. */ // Objective-C PFQuery *query = [PFQuery queryWithClassName:@"Comments"]; [query whereKey:@"post" equalTo:@123]; [[[self findAsync:query] continueWithBlock:^id(BFTask *results) { // Collect one task for each delete into an array. NSMutableArray *tasks = [NSMutableArray array]; for (PFObject *result in results) { // Start this delete immediately and add its task to the list. [tasks addObject:[self deleteAsync:result]]; } // 所有的删除任务合在一起本身也是一个任务,删除任务之前是并行的 return [BFTask taskForCompletionOfAllTasks:tasks]; }] continueWithBlock:^id(BFTask *task) { // Every comment was deleted. return nil; }];
-
任务执行者
BFExecutor 是 BFTask的执行者,默认的BFExecutor是立即在当前线程中执行的,但是如果call stack太深,会异步dispatch到global queue上去执行。
如果不用默认的执行者,还可以指定执行queue,或者是NSOperationQueue,包括主线程。// 创建 BFExecutor BFExecutor *myExecutor = [BFExecutor executorWithBlock:^void(void(^block)()) { dispatch_async(dispatch_get_main_queue(), block); }]; BFExecutor *myExecutor = [BFExecutor mainThreadExecutor]; // And use the Main Thread Executor like this. The executor applies only to the new // continuation being passed into continueWithBlock. [[self fetchAsync:object] continueWithExecutor:myExecutor withBlock:^id(BFTask *task) { myTextView.text = [object objectForKey:@"name"]; }];
-
任务取消
我们在直接使用GCD的时候很难把一个block任务取消。BFCancellationTokenSource就很方便的实现了任务取消功能。
/* * 执行任务的时候传入BFCancellationTokenSource,在任务还没有执行的时候设置取消标示, * 该任务的block不会被执行 */ BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource]; /// 任务延迟执行100毫秒 BFTask *task = [BFTask taskWithDelay:100]; task = [task continueWithExecutor:[BFExecutor immediateExecutor] block:^id(BFTask *t) { NSLog(@"Continuation block should not be triggered"); return nil; } cancellationToken:cts.token]; /// 设置取消标示 [cts cancel]; /// 等待任务完成 (这个代码一般不在主线程中使用) [task waitUntilFinished];
/* * 也可以在continueWithBlock中自己实现对cancellationToken的判断,取消任务 */ - (void)doSomethingComplicatedAsync:(MYCancellationToken *)cancellationToken { [[self doSomethingAsync:cancellationToken] continueWithBlock:^{ if (cancellationToken.isCancelled) { return [BFTask cancelledTask]; } // Do something that takes a while. return result; }]; } // Somewhere else. MYCancellationToken *cancellationToken = [[MYCancellationToken alloc] init]; [obj doSomethingComplicatedAsync:cancellationToken]; // When you get bored... [cancellationToken cancel];
-
关键源码解析
BFTask的关键代码就是下面这个函数。
/* * 在指定的BFExecutor中执行block代码 */ - (BFTask *)continueWithExecutor:(BFExecutor *)executor block:(BFContinuationBlock)block cancellationToken:(nullable BFCancellationToken *)cancellationToken { // 创建BFTaskCompletionSource,返回source的task。本次执行block的操作也是一个需要返回的任务。 BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; // 定义执行的executionBlock dispatch_block_t executionBlock = ^{ // 如果cancellationToken标示位取消,就直接把tcs中的task设置为取消,不会继续执行BFContinuationBlock,实现了对block的取消操作。 if (cancellationToken.cancellationRequested) { [tcs cancel]; return; } id result = nil;
pragma clang diagnostic push
pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (BFTaskCatchesExceptions()) {
@try {
// 执行BFContinuationBlock,如果block中抛出异常就把tcs中的task设置tcs中task为异常,不再继续执行
result = block(self);
} @catch (NSException *exception) {
tcs.exception = exception;
return;
}
} else {
result = block(self);
}
pragma clang diagnostic pop
// 如果block也返回一个BFTask,如果BFTask已经结束了,执行setupWithTask(BFTask)
// 以BFTask的结果作为tcs的结果。如果BFTask没有结束,就等BFTask结束后再
// 执行setupWithTask block
if ([result isKindOfClass:[BFTask class]]) {
id (^setupWithTask) (BFTask *) = ^id(BFTask *task) {
if (cancellationToken.cancellationRequested || task.cancelled) {
[tcs cancel];
pragma clang diagnostic push
pragma clang diagnostic ignored "-Wdeprecated-declarations"
} else if (task.exception) {
tcs.exception = task.exception;
pragma clang diagnostic pop
} else if (task.error) {
tcs.error = task.error;
} else {
tcs.result = task.result;
}
return nil;
};
BFTask *resultTask = (BFTask *)result;
if (resultTask.completed) {
setupWithTask(resultTask);
} else {
[resultTask continueWithBlock:setupWithTask];
}
} else {
tcs.result = result;
}
};
BOOL completed;
// 如果self没有completed,就把执行executionBlock的操作放在callbacks里。
// task中对callbacks的访问需要加锁
@synchronized(self.lock) {
completed = self.completed;
if (!completed) {
[self.callbacks addObject:[^{
[executor execute:executionBlock];
} copy]];
}
}
if (completed) {
[executor execute:executionBlock];
}
return tcs.task;
}
```