我有一个使用此方法填充的简单TableViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSInteger row = [indexPath row];
cell.textLabel.text = [[NSString alloc] initWithFormat:@"%d", row];
dispatch_async(dispatch_get_main_queue(), ^{
[cell setNeedsLayout];
});
});
return cell;
}
在第一次加载时,数字被正确显示。 当我收看视图时,数字随机显示(然后它们被正确排序)。
为什么,滚动,数字没有排序?
答案 0 :(得分:3)
UIKit
不是线程安全的。您无法在另一个线程上设置UIImageView
的图像,同样,您也无法在另一个线程上设置UILabel
的文本。试试这个:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSInteger row = [indexPath row];
[cell.textLabel performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"%d", row] waitUntilDone:YES];
});
但就个人而言,我不明白你为什么要使用派遣。从整数创建字符串的开销很小,除非幕后处理的内容比此处显示的要多。
您可以改为使用此代替调度调用:
NSInteger row = [indexPath row];
cell.textLabel.text = [NSString stringWithFormat:@"%i", row];
答案 1 :(得分:3)
我想通过阅读你正在尝试做的事情,你会以错误的方式稍微探讨一下。您不应该在cellForRowAtIndexPath
中执行异步操作,因为您无法确定稍后要更新的单元格是否正确。
考虑你的代码:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
...
return cell;
}
所以你要创建一个单元并返回它。它可能是重用队列中的一个或新的队列。到现在为止还挺好。但是你添加了这段代码:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSInteger row = [indexPath row];
cell.textLabel.text = [[NSString alloc] initWithFormat:@"%d", row];
dispatch_async(dispatch_get_main_queue(), ^{
[cell setNeedsLayout];
});
});
所以你现在派遣到后台队列来设置那个单元格上的东西。但是如果发生这些事件会发生什么:
表格要求提供一个单元格。
创建新单元格,单元格A,调度完成后再设置。
用户滚动和单元格A从视图中删除并放入重用队列。
表格要求提供一个单元格。
从重用池返回Cell A,并将另一个dispatch放入队列。
首次发送完成,在单元格A上设置内容。
这不是你想要的,因为现在单元格A上有旧数据。当然,如果你的队列是串行的,你可能可以逃脱它,因为第二个调度将始终在第一个之后完成,但是a)这不好,因为无论如何都会看到过时的数据而b)你的队列可能是并行队列反正。
那么,你问什么是正确的解决方案?好吧,我通常采用的方法是定制单元格。听起来你想让单元格加载一个图像,我假设你想从网络加载该图像?所以我这样做的方法是在其上有一个loadImage
方法的自定义单元格,当表格视图停止滚动并且没有减速,或者表格视图停止减速时,在每个可见单元格上调用该单元格。 (请参阅UIScrollViewDelegate
方法,了解我的意思。)
然后在loadImage
方法中,我触发HTTP请求以获取图像并等待它返回并将我的自定义单元格中的UIImageView
设置为返回的图像。关键的额外一点是我在自定义单元格上有一个setter来设置对象/ URL /单元格显示的内容,它提供了有关要下载哪个图像的信息。然后,当单元格获得新URL请求时,它会取消旧URL并在下一次loadImage
调用中启动新URL。
希望这是有道理的!我实际上做的略有不同,但这是一个简单的描述,因为我可以给出一般的想法。遗憾的是,我没有任何代码可以显示,但请提出任何问题,我会尝试再填写一些问题。