美文网首页iOS
Bolts/BFTask

Bolts/BFTask

作者: 卤蛋也幸福 | 来源:发表于2017-02-26 22:50 被阅读0次

    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;
    

    }

    ```

    相关文章

      网友评论

        本文标题:Bolts/BFTask

        本文链接:https://www.haomeiwen.com/subject/yzaowttx.html