表视图单元格需要异步接收的事实(计数)。在
cellForRowAtIndexPath
,但这不是很好的做法:(a)当用户滚动时,对的请求将被反复激发,(b)在请求完成时,需要该事实的单元格可能会被重用(可能对应于不同的行)。这里有一个更好的模式:
隔离网络代码,只是为了保持清醒:
- (void)commentCountForPost:(PFObject *)post withCompletion:(void (^)(NSNumber *))completion {
PFQuery *commentsQuery = [PFQuery queryWithClassName:@"Comment"];
[commentsQuery whereKey:@"photo" equalTo:post];
NSLog(@"sement: %@", commentsQuery);
[commentsQuery findObjectsInBackgroundWithBlock:^(NSArray *array, NSError *error) {
completion(@(array.count)); // wrap as an NSNumber
}];
}
缓存结果,以便每行最多请求一次:
// keys will be indexPaths, values will be comment counts
@property(nonatomic,strong) NSMutableDictionary *commentCounts;
// be sure to initialize early to
self.commentCounts = [@{} mutableCopy];
现在进入
单元格行索引路径
,请记住两件重要的事情:(a)检查缓存中是否有已获取的值,(b)不要将单元格保留在完成块中,它可能在块运行时引用了错误的行。相反,重新加载行,知道缓存的值将在那里:
// ...
PFObject *post = postArray[indexPath.row];
// ...
[cell.likeLabel setText:@""];
NSNumber *commentCount = self.commentCounts[indexPath];
if (commentCount) {
self.likeLabel.text = [NSString stringWithFormat:@"%@ comments", commentCount];
} else {
[self commentCountForPost:post withCompletion:^(NSNumber *count) {
self.commentCounts[indexPath] = count; // cache the result
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
}
return cell;
有时我会将缓存逻辑添加到网络代码中。如何做到这一点应该是显而易见的,但如果你愿意,我可以演示。
编辑
希望您可以从解决方案的逻辑中看到,当服务器数据发生更改时,客户端的缓存将过时,应该丢弃。什么时候
这
视图控制器知道更改,它可以这样做:
// imagine we know the comment count changed at a specific indexPath
[self.commentCounts removeObjectAtIndex:indexPath.row];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]];
// or, imagine we know that the comment count changed someplace, or in more than one places. call this...
- (void)emptyCacheAndReloadData {
[self.commentCounts removeAllObjects];
[self.tableView reloadData];
}
但如果
另一个
视图控制器进行了更改,这个vc需要了解它,这是一个不同的问题,这是SO上经常问到的问题。我鼓励你阅读
answer given here
,这是正确的,也是相当全面的。如果这是你第一次处理这个话题,你可能——可以理解——想先尝试一个小捷径。这是这样的(符合你对viewWillAppear的直觉):
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self emptyCacheAndReloadData];
}
编辑2
这里描述的延迟加载和缓存方法会在每个表单元需要显示时花费大量精力来执行异步工作。一旦为一行初始化了缓存,该行的显示速度很快,但在第一次滚动时,表会感觉有点颠簸。
我们必须在某个地方做计数工作,最好的地方可能是保存评论后的云端。在那里,我们可以抓取评论所属的帖子,计算其总评论数,并将总数保存在帖子上。有了这个,你可以跳过我上面的整个解决方案,只需要说一些像。。。
self.likeLabel.text = [NSString stringWithFormat:@"%@ comments", post[@"commentCount"]];
但这假设您正在使用云代码维护Post上的注释计数属性。如果没有云代码,我们需要将初始工作转移到客户端的其他地方。它必须发生在帖子之后(你的
postArray
)但在重新加载表视图之前。在代码中找到那个位置,然后调用这样的函数。。。
- (void)postsLoaded {
// build an array of indexPaths in your table. this is just a row for each post
NSMutableArray *indexPaths = [@[] mutableCopy];
for (int i=0; i<self.postArray.count; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
[indexPaths addObject:indexPath];
}
// now preload the counts
[self preloadCountsForIndexPaths:indexPaths completion:^(BOOL success) {
[self.tableView reloadData];
}];
}
// this is a recursive method. to count comments on array of posts
// count them on the first post, then count comments on the rest
- (void)preloadCountsForIndexPaths:(NSArray *)indexPaths completion:(void (^)(BOOL))completion {
if (indexPaths.count == 0) return completion(YES);
NSIndexPath *indexPath = indexPaths[0];
NSArray *remainingIndexPaths = [indexPaths subarrayWithRange:NSMakeRange(1, indexPaths.count-1)];
PFObject *post = self.postArray[indexPath.row];
[self commentCountForPost:post withCompletion:^(NSNumber *count) {
self.commentCounts[indexPath] = count; // cache the result
[self preloadCountsForIndexPaths:remainingIndexPaths completion:completion];
}];
}