1

私のアプリでは、GameCenter プレイヤー ID をエイリアスに変換しようとしています。ID を受け取ったら、ループから次のメソッドを呼び出します。メソッドは一度に 1 つの ID で呼び出され、単一のエイリアスを返す必要があります。単一のエイリアスを返そうとしていますが、それを機能させることができません。最初はリターンをブロックに入れましたが、ブロック内でリターンできないことがわかりました。これで、オブジェクトを配列に追加してブロックの後に戻るように設定しましたが、次のエラーが表示されます: [__NSArrayM objectAtIndex:]: インデックス 0 が空の配列の境界を超えています。どんな助けでも大歓迎です。

@property (nonatomic, strong, retain) NSMutableArray * playerScores;

.

-(void)getScoresAndAliasForLeaderboard{
 [GKLeaderboard loadLeaderboardsWithCompletionHandler:^(NSArray *leaderboards, NSError *nsError) {
        if( nsError != nil )
        {
            //error( nsError, "get leaderboard score" ) ;
            return ;
        }

        for( GKLeaderboard* board in leaderboards )
        {
            board.timeScope = GKLeaderboardTimeScopeAllTime;
            board.playerScope = GKLeaderboardPlayerScopeGlobal;
            board.range = NSMakeRange(1, 100);

            [board loadScoresWithCompletionHandler:^(NSArray *scores, NSError *error) {
                 NSLog(@"board: %@",board.description);

                NSLog(@"error: %@",error);
                if (error == nil)
                {
                    for (GKScore *s in scores)
                    {

                        GCLeaderboardScore *playerScore = [[GCLeaderboardScore alloc] init];
                        playerScore.playerID = s.playerID;
                        playerScore.score = (int)s.value;
                        playerScore.rank = s.rank;
                        NSArray * player = [[NSArray alloc]initWithObjects:s.playerID, nil];
                        [self loadPlayerData:player];
                        playerScore.alias = [_playerScores objectAtIndex:0];
                        NSLog(@"alias: %@, score: %d",playerScore.alias ,playerScore.score);
                        [_dataMutableArray addObject: playerScore];
                    }
                    [self.tableView reloadData];
                }
            }];


        }
                }] ;

}

.

-(NSString*) loadPlayerData: (NSArray *) identifiers
{
    if(!_playerScores){
    _playerScores = [[NSMutableArray alloc]init];
    }
    [GKPlayer loadPlayersForIdentifiers:identifiers withCompletionHandler:^(NSArray *players, NSError *error)
     {

         if (error != nil)
         {
             // Handle the error.
         }
         if (players != nil)
         {
                 GKPlayer* player = [players objectAtIndex:0];
                 [_playerScores addObject:player.alias];
         }
     }];

    return [_playerScores objectAtIndex:0];
}
4

3 に答える 3

0

loadPlayersForIdentifiers: withCompletionHandler:プレイヤーを非同期的にロードするため、完了ハンドラーがあります。この時点では[_playerScores objectAtIndex:0]、完了ハンドラはまだ呼び出されておらず、playerScores空です。これが、表示されるエラーの理由です。

どのソリューションでも、非同期は非同期であることを受け入れる必要があります。非常に単純なソリューションの場合、loadPlayerData:何も返さない可能性があります ( void)。_playerScoresすでにメンバー変数のように見え、後で繰り返し調べることができます。

編集:参照は、完了ハンドラーがメインスレッドで呼び出されることを明示的に述べています:

その後、タスクが完了すると、Game Kit は完了ハンドラーを呼び出します。完了ハンドラは、常にメイン スレッドで呼び出されます。

[self.tableView reloadData];エイリアスをロードする完了ハンドラ内から安全に呼び出すことができるので、これは素晴らしいことです。ただし、あなたの編集から、さらに 2 つの問題が明らかになります。

  1. の完了ハンドラのfor-loop内で、に渡すプレーヤー IDの配列を繰り返しインスタンス化します。最初にこの配列を埋めてから、すべてのプレーヤー ID を一度に呼び出す方がはるかに優れています。scoresloadScoresWithCompletionHandler:playerloadPlayerData:loadPlayerData:

  2. を呼び出しても、[self.tableView reloadData]非同期で動作するloadPlayerData:ため、ジョブはまだ完了していません。loadPlayersForIdentifiers:

簡単な修正として、プレイヤー エイリアスのクエリ方法を合理化し、プレイヤー エイリアスが Game Center サーバーから到着した後にのみテーブル ビューを更新することをお勧めします。

[board loadScoresWithCompletionHandler:^(NSArray *scores, NSError *error) {
    if (error)
    {
        return;
    }
    NSArray *player_ids = [[NSMutableArray alloc] init];
    for (GKScore *s in scores)
    {
        [player addObject:s.playerID];
    }
    [self loadPlayerData:player];
}];

ポイントをより明確にするために、実際のスコアの取り扱いを省略しました。これで、プレイヤー エイリアスは 1 回だけ読み込まれます (リーダー ボードが複数ある場合は、別の問題であり、個別に対処する必要がある場合があります)。

-(void)loadPlayerData:(NSArray *)identifiers
{
    if (! _playerAliases) {
        _playerAliases = [[NSMutableDictionary alloc]init];
    }
    [GKPlayer loadPlayersForIdentifiers:identifiers
        withCompletionHandler:^(NSArray *players, NSError *error)
    {
        if ((error) || (! players))
        {
            return;
        }
        for (GKPlayer p in players)
        {
            [_playerAliases setObject:player.alias forKey::player.id];
        }
        [self.tableView reloadData];
    }];
}

これで、辞書を使用し_playerAliasesてプレイヤー ID のニックネームを検索できるようになりました。loadPlayerData:スコアをロードする完了ハンドラー内から呼び出すため、すべてのデータ完了後に準備が整いloadPlayersForIdentifiers:ます。したがって、これはテーブル ビューの更新をトリガーするのに便利な時期です。

于 2013-11-09T19:31:04.643 に答える