我有一个以UIViewController作为子视图的tableView。表视图有三个部分,我使用了问题PFQueryTableViewController with 3 Sections的答案来完成这一任务,但是数据并没有被加载到表中。我正在viewWillAppear的后台执行我的查询。我担心这是由于嵌套的查询块造成的。我怎么才能解决这个问题?这是我的代码:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
PFQuery *gameQuery = [PFQuery queryWithClassName:@"Game"];
[gameQuery whereKey:@"players" equalTo:[PFUser currentUser]];
[gameQuery orderByDescending:@"createdAt"];
[gameQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
self.myTurn = [NSMutableArray array];
self.theirTurn = [NSMutableArray array];
self.gameOver = [NSMutableArray array];
self.allGames = [NSArray array];
for(PFObject *object in objects)
{
if([object objectForKey:@"isOver"] == [NSNumber numberWithBool:YES])
{
[self.gameOver addObject:object];
}
else
{
PFRelation *relation = [object relationForKey:@"whoseTurn"];
PFQuery *relQuery = [relation query];
[relQuery findObjectsInBackgroundWithBlock:^(NSArray *userObjects, NSError *error1){
NSMutableArray *arr = [NSMutableArray array];
for(PFUser *user in userObjects)
{
[arr addObject:user.objectId];
}
if([arr containsObject:[PFUser currentUser].objectId])
{
[self.myTurn addObject:object];
}
else
[self.theirTurn addObject:object];
}];
}
}
self.allGames = [NSArray arrayWithObjects:self.myTurn, self.theirTurn, self.gameOver, nil];
[self.tableView reloadData];
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.allGames count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self.allGames objectAtIndex:section] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
PFObject *object = self.allGames[indexPath.section][indexPath.row];
cell.textLabel.text = [object objectForKey:@"numPlayers"];
return cell;
}发布于 2015-02-12 09:46:02
下面是你可以做的事情:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
PFQuery *gameQuery = [PFQuery queryWithClassName:@"Game"];
[gameQuery whereKey:@"players" equalTo:[PFUser currentUser]];
[gameQuery orderByDescending:@"createdAt"];
[gameQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
@synchronized(self.allGames) {
self.allGames = [NSArray arrayWithObjects: [NSMutableArray array], [NSMutableArray array], [NSMutableArray array], nil];
// These two instance variables contain values used in asynchronous blocks, to know how much responses are still expected
self.expectedNbGames = [objects count];
self.fetchedNbGames = 0;
}
for(PFObject *object in objects)
{
if([object objectForKey:@"isOver"] == [NSNumber numberWithBool:YES])
{
@synchronized(self.allGames) {
[((NSMutableArray *)self.allGames[2]) addObject:object];
self.fetchedNbGames++;
}
}
else
{
PFRelation *relation = [object relationForKey:@"whoseTurn"];
PFQuery *relQuery = [relation query];
[relQuery findObjectsInBackgroundWithBlock:^(NSArray *userObjects, NSError *error1){
NSMutableArray *arr = [NSMutableArray array];
for(PFUser *user in userObjects)
{
[arr addObject:user.objectId];
}
@synchronized(self.allGames) {
if([arr containsObject:[PFUser currentUser].objectId])
{
[((NSMutableArray *)self.allGames[0]) addObject:object];
}
else
[((NSMutableArray *)self.allGames[1]) addObject:object];
}
self.fetchedNbGames++;
if (self.fetchedNbGames == self.expectedNbGames)
{
// We have now received the last expected response, it's time to sort everything again in descending order based on the "createdAt" value
NSSortDescriptor *dateDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:dateDescriptor];
// self.allGames[0] is already sorted anyway because it's been left untouched from the initial PFQuery
self.allGames[1] = [self.allGames[1] sortedArrayUsingDescriptors:sortDescriptors];
self.allGames[2] = [self.allGames[2] sortedArrayUsingDescriptors:sortDescriptors];
// And... reload one last time!
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}];
}
}
[self.tableView reloadData];
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
@synchronized(self.allGames) {
return [self.allGames count];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
@synchronized(self.allGames) {
return [[self.allGames objectAtIndex:section] count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
@synchronized(self.allGames) {
PFObject *object = self.allGames[indexPath.section][indexPath.row];
cell.textLabel.text = [object objectForKey:@"numPlayers"];
}
return cell;
}@synchronized()构造用于确保线程安全,因为您将从不同的线程(主线程,但也是来自解析答复的异步块)访问(读写) sell.allGames数组。
请注意,我们要从异步块中多次分派reloadData,因为您的代码目前无法知道所有块何时完成运行。我们需要在主线程上分派这个调用,因为它将触发UI更新,而这些更新总是必须在主线程上完成。
看看这是否有效,也许您可以使用属性来稍微改进它(避免每次使用@synchronized()时编写self.allGames)。
UPDATE:我改进了我的答案,提供了一种在收到所有回复后重新排序的方法。这个过程涉及创建两个新的实例变量(名为fetchedNbGames和expectedNbGames的整数),它们必须在收到来自Parse的每个响应后进行更新和相互比较。一旦接收到所有响应,结果数组就可以重新排序(由于请求被异步发送和处理,结果数组可能被无序排序),表视图再次刷新。
这是未经测试的,可能有很大的改进空间,但这取决于您调整它以满足您的需要,您得到了基本的想法。
还请参阅this other SO thread,以更直观地解释与线程和异步调用相关的问题。
https://stackoverflow.com/questions/28470167
复制相似问题