0

バックグラウンドで SpriteKit 描画を実行するために BFTasks を使用していますが、描画がメイン スレッドをロックしているため、それらを正しく使用しているかどうかはわかりません。

各オブジェクトは複数の SKSpriteNode で構成され、レンダリング前にフラット化されます。フラット化されるとすぐに、つまり呼び出したときにそれぞれをレンダリングしたいと思います[self addChild:miniNode];が、すべてが作成されるまで待機し (メインスレッドをロック)、一度に表示されます。

一連のタスクを示すために、以下のコードを簡略化しました。

- (void)drawLocalRelationships
{
    [ParseQuery getLocalRelationships:_player.relationships block:^(NSArray *objects, NSError *error) {
        [[[self drawRelationships:objects forMini:_player]
          continueWithBlock:^id(BFTask *task) {
              //this continues once they've all been drawn and rendered 
              return nil;
          }];
    }];
}

- (BFTask *)drawRelationships:(NSArray *)relationships forMini:(Mini *)mini
{
    return [_miniRows drawSeriesRelationships:relationships forMini:mini];
}

MiniRows クラス:

- (BFTask *)drawSeriesRelationships:(NSArray *)relationships forMini:(Mini *)mini
{
    BFTask *task = [BFTask taskWithResult:nil];

    for (Relationship *relationship in relationships) {
        task = [task continueWithBlock:^id(BFTask *task) {
            return [self drawRelationship:relationship mini:mini];
        }];
    }
    return task;
}

- (BFTask *)drawRelationship:(Relationship *)relationship mini:(Mini *)mini
{
    //code to determine 'row'
    return [row addMiniTask:otherMini withRelationship:relationship];
}

行クラス:

- (BFTask *)addMiniTask:(Mini*)mini withRelationship:(Relationship *)relationship
{
    //drawing code
    MiniNode *miniNode = [self nodeForMini:mini size:size position:position scale:scale];
    [self addChild:miniNode]; //doesn't actually render here
    return [BFTask taskWithResult:nil];
}

バックグラウンド スレッドで addMiniTask メソッドを実行してみましたが、違いはないようです。BFTask の概念を誤解しているのではないかと思います。バックグラウンド スレッドで自動的に実行されると思いましたが、そうではないのでしょうか。

4

1 に答える 1

3

デフォルトでは、BFTask はバックグラウンド スレッドでは実行されません。

もしあなたがそうするなら:

BFTask * immediateTask = [BFTask taskWithResult: @"1"];

immediateTask は現在のスレッドですぐに完了します。つまり、completed プロパティは YES です。

また、次の場合:

[task continueWithBlock:^id(BFTask *task) {
    // some long running operation 
    return nil;
}];

タスクが完了すると、ブロックはデフォルトのエグゼキュータで実行されます。コール スタックが深すぎない限り、ブロックは現在のスレッドですぐに実行されます。深い場合はバックグラウンド ディスパッチ キューにオフロードされます。現在のスレッドは、continueWithBlock が呼び出されたスレッドです。そのため、バックグラウンド スレッドで前のコードを呼び出していない限り、実行時間の長い操作によって現在のスレッドがブロックされます。

ただし、明示的なエグゼキューターを使用して、ブロックを別のスレッドまたはキューにオフロードできます。

BFTask * task = [BFTask taskFromExecutor:executor withBlock:^id {
    id result = ...; // long computation
    return result;
}];

適切なエグゼキューターを選択することが重要です。

  • executor = [BFExecutor defaultExecutor] タスクのブロックは現在のスレッド (タスクの作成が実行されるスレッド) で実行されるか、コール スタックが深すぎる場合はバックグラウンド キューにオフロードされます。そのため、何が起こるかを予測するのは困難です。
  • executor = [BFExecutor immediateExecutor] タスクのブロックは、前のタスクと同じスレッドで実行されます (以下の連鎖を参照)。しかし、前のタスクがデフォルトのエグゼキューターによって実行された場合、それがどのスレッドであるかはわかりません。
  • executor = [BFExecutor mainThreadExecutor] タスクのブロックはメイン スレッドで実行されます。これは、長時間実行された操作の後に UI を更新するために使用するものです。
  • executor = [BFExecutor executorWithDispatchQueue:gcd_queue] タスクのブロックは、指定された gcd キューで実行されます。実行時間の長い操作を実行するには、バックグラウンド キューを使用してキューを作成します。キューのタイプ (シリアルまたはコンカレント) は、実行するタスクとその依存関係によって異なります。

エグゼキュータに応じて、異なる動作が得られます。

BFTasks の利点は、異なるスレッドで実行されているタスクをチェーンして同期できることです。たとえば、バックグラウンド操作を長時間実行した後にメイン スレッドで UI を更新するには、次のようにします。

// From the UI thread
BFTask * backgroundTask = [BFTask taskFromExecutor:backgroundExecutor withBlock:^id {
    // do your long running operation here
    id result = ...; // long computation here
    return result;
}];
[backgroundTask continueWithExecutor:[BFExecutor mainThreadExecutor] withSuccessBlock:^id(BFTask* task) {
    id result = task.result;
    // do something quick with the result - we're executing in the UI thread here
    return nil
}];

PFQuery の findInBackgroundWithBlock メソッドはデフォルトのエグゼキュータでブロックを実行するため、メイン スレッドからそのメソッドを呼び出すと、ブロックもメイン スレッドで実行される可能性が高くなります。あなたの場合、SpriteKit については何も知りませんが、すべてのスプライトを取得してから UI を更新します。

- (void)queryRenderAllUpdateOnce {

    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"current thread is %@ ", currentThread);

    // replace the first task by [query findObjectsInBackground]
    [[[BFTask taskFromExecutor:[Tasks backgroundExecutor] withBlock:^id _Nonnull{

        NSLog(@"[%@] - Querying model objects", [NSThread currentThread]);
        return @[@"Riri", @"Fifi", @"LouLou"];

    }] continueWithExecutor:[BFExecutor immediateExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {

        NSLog(@"[%@] - Fetching sprites for model objects", [NSThread currentThread]);
        NSArray<NSString *> * array = task.result;
        NSMutableArray * result = [[NSMutableArray alloc] init];
        for (NSString * obj in array) {
            // replace with sprite 
            id sprite = [@"Rendered " stringByAppendingString:obj];
            [result addObject:sprite];
        }
        return result;

    }] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {

        NSLog(@"[%@] - Update UI with all sprite objects: %@", [NSThread currentThread], task.result);
        // TODO update the UI here.
        return nil;
    }];

}

しかし、このソリューションでは、すべてのスプライトがフェッチ (フラット化?) されてから、UI が更新されます。UI を更新したい場合は、スプライトがフェッチされるたびに、次のようにすることができます。

- (void)queryRenderUpdateMany {

    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"current thread is %@ ", currentThread);

    [[[BFTask taskFromExecutor:[Tasks backgroundExecutor] withBlock:^id _Nonnull{

        NSLog(@"[%@] - Querying model objects", [NSThread currentThread]);
        return @[@"Riri", @"Fifi", @"LouLou"];

    }] continueWithExecutor:[BFExecutor immediateExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {

        NSArray<NSString *> * array = task.result;
        NSMutableArray * result = [[NSMutableArray alloc] init];
        for (NSString * obj in array) {

            BFTask *renderUpdate = [[BFTask taskFromExecutor:[BFExecutor immediateExecutor] withBlock:^id _Nonnull{

                NSLog(@"[%@] - Fetching sprite for %@", [NSThread currentThread], obj);
                return [@"Rendered " stringByAppendingString:obj];

            }] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {

                NSLog(@"[%@] - Update UI with sprite %@", [NSThread currentThread], task.result);
                return nil;

            }];
            [result addObject: renderUpdate];
        }

        return [BFTask taskForCompletionOfAllTasks:result];

    }] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"[%@] - Updated UI for all sprites", [NSThread currentThread]);
        return nil;
    }];

}

ここで、中間タスクは、すべての renderUpdate タスクが完了すると完了するタスクを作成します。

この助けを願っています。

B

于 2016-01-26T16:00:54.127 に答える