好像我的应用程序正在以错误的顺序执行操作

时间:2013-05-03 20:47:49

标签: ios objective-c cocoa-touch completionhandler

(注意:你甚至可以在不阅读我的任何代码的情况下回答这个问题,如果你不想阅读这个问题的小说,我已在底部解释过了)

我有一些代码让我有点偏离正轨。看起来我的代码在数据准备好之前调用了[tableView reloadData],尽管我在调用reloadData之前设置了数据。这可能是一个新手问题,但我以前从未处理过这种代码,如果有人能够向我解释到底发生了什么,以及为什么我的某些代码可以跳出来,我会很高兴按时间顺序排列的逻辑..

我的问题与GameCenter无关,我认为这是一个普遍的Objective-C事情,但是我正在体验GameCenter,所以即使你对GameCenter一无所知,你也许能够明白我在说什么。

我有一个tableViewController,我正在用GameCenter中的匹配(游戏)填充tableView,并显示对手的名字(在我的回合制游戏中总是一个对手)。

我把它放在我的viewDidAppear

[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error) {       
//Gets an array (*matches) of GKTurnBasedMatches(All matches the local player is involved in)
    if(error)
    {
        NSLog(@"Load matches error: %@", error.description);
        return;
    }
    //putting them in an array
    matchList = [[NSArray alloc] initWithArray:matches];      

    //So far only for testing purposes: I am creating an array for player names:
    playerNames = [[NSMutableArray alloc] init];
    for(GKTurnBasedMatch* mat in matchList)
    {//For every match in the array of matches, find the opponent:
        GKTurnBasedParticipant *otherParticipant; //These games only have 2 players. Always.
        for(GKTurnBasedParticipant *part in [mat participants])
        { //Loop through both participants in each match
            if(NO == [part.playerID isEqualToString:[GKLocalPlayer localPlayer].playerID])
            {//If the participant *part is NOT the local player, then he must be the opponent:
                otherParticipant = part; //Save the opponent.
            }   
        }

        if(otherParticipant != nil && otherParticipant.playerID != nil)
        { //If the opponent exists(like he does)
            [GKPlayer loadPlayersForIdentifiers:@[otherParticipant.playerID] withCompletionHandler:^(NSArray *players, NSError *error) {
               //Returns an array with only the opponent in it
                if([(GKPlayer*)[players objectAtIndex:0] displayName]!=nil)
                {//If he has a name:
                    NSString *nam = [[NSString alloc] initWithString:[(GKPlayer*)[players objectAtIndex:0] displayName]];
                    //Add the name to the name-array
                    [playerNames addObject:nam];
                }
                else
                {//If he doesn't have a name, put "No name" into the name array(is not happening)
                    NSString *nam = [[NSString alloc]initWithString:@"No name"];
                    [playerNames addObject:nam];
                }     
            }];
        }
        else
        {//If for some reason the entire opponent is nil or he doesn't have a playerID, which isn't happening, add "No name";(This is not happening)
            [playerNames addObject:[NSString stringWithFormat:@"No name"]];
        } 
    }
    //And THEN reload the data, now with the NSMutableArray 'playerNames' ready and filled with opponent's names.
    [[self tableView] reloadData];
}];

这是我的CellForRowAtIndexPath

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"matchCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    NSString *name = @"Nobody";
    if(playerNames!=nil && [playerNames count]>indexPath.row)
        name = [playerNames objectAtIndex:indexPath.row];
    [[cell textLabel] setText:[NSString stringWithFormat:@"Game vs. %@", name]];

    return cell;
}

事情是:当视图控制器出现时:正如预期的那样,tableView最初是空的,但在大约半秒之后,它会以正确的数量填充“Game vs. Nobody”游戏。如果我开始两个游戏,然后重新进入视图控制器,那么它首先是空白的,然后在两个单元格上显示“Game vs. Nobody”。 BUT:如果我在屏幕外滚动行,并强制他们再次呼叫cellForRowAtIndexPath,则会显示正确的名称。所以看起来[tableView reloadData]中的viewDidAppear在我要求它之前被调用,在具有名称的数组被填充之前。

我认为这与整个CompletionHandler-thing有关,因为我之前从未使用过这个,但我认为所有内部的CompletionHandler应该按时间顺序发生?有什么我可以打电话的,比如onCompletion{}或其他什么?

理想情况下,我想要一个UIAlertView或其他东西来显示一个旋转轮,UIActivityIndicationView,我希望它显示,直到所有数据都已正确加载。我不知道如何找出 WHEN completeHandler完成,以及它到底是什么..

1 个答案:

答案 0 :(得分:1)

playerNames数组正在loadPlayersForIdentifiers的完成处理程序中填充,这是(我假设)异步。在完成处理程序运行之前,不会填充names数组,但是您在另一个块中调用[tableView reloadData]

要修复,只需在loadPlayersForIdentifiers的完成块内移动重载调用(最后)