我们的UITableView返回给定部分的行数。我们所遇到的是,当调用cellForRowAtIndexPath时,数字已经改变,所以我们最终得到数组索引超出界限。
是否有一种同步这两种方法的好方法,因此底层数据不会改变?我们考虑使用@synchronized,但不确定何时释放锁。
我们正在做的另一件事是刷新表,这是来自一个单独的线程。
[self addUsers:usersToShow];
[[self invokeOnMainThreadAndWaitUntilDone:NO] refresh:self]; // is this the issue?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.users.count; // returns 10 for example
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *newCell= nil;
static NSString *cellId = @"cellId";
cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:@"MyCell" owner:self options:nil];
cell = newCell;
self.newCell = nil;
}
User* user = [self.users objectAtIndex:indexPath.row]; // index out of bounds now because number of users has changed
}
答案 0 :(得分:2)
由于您已经为自己解决了问题,因此无法使用@synchronized,因为范围超出了方法范围。
您不希望尝试使用Lock对象,将其锁定在numberOfRowsInSection中并将其解锁为cellForRowAtIndexPath。它不会起作用。你需要做的是确保你在cellForRowAtIndexPath中做你需要的任何锁定,并处理传入的行可能不再有效的事实,例如:
User * user = nil;
@synchronized(self.users) {
if (indexPath.row < [self.users count]) {
user = [self.users objectAtIndex:indexPath.row];
}
}
if (user) {
//configure cell
}
else {
//set cell fields to be blank
}
您是否尝试仅在主线程中更新模型(self.users)?这应该减少对模型更新的可能性,因为它与对getNumberOfRows和configureCellAt的调用交错。在您给出的示例中,您正在随机线程上更新模型,然后在主线程上重新加载数据。我建议确保你的模型是线程安全的(或仅在主线程中更新/读取)。
答案 1 :(得分:2)
尽管大脑已经回答了,但我想用示例代码强调“主线程中的更新模型”。
您可能会遇到此问题,因为您的模型在某些后台线程中已更改。时间表应如下所示:
{NSThread number = 1,name = main} - [ViewController tableView:numberOfRowsInSection:](例如返回10)
{NSThread number = 8,name =(null)} - [ViewController changeTheModel](从模型中删除一些对象,或获取少于10个对象的新模型)
{NSThread number = 1,name = main} - [ViewController tableView:cellForRowAtIndexPath:](索引超出绑定异常,因为第10个对象不存在)
要解决此问题,您应该在更改模型时执行以下操作:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray* items = [self getNewModel];// get new model on background thread
dispatch_async(dispatch_get_main_queue(), ^ {
self.items = items;// replace the model with new one on main thread
[self.tableView reloadData];// refresh table without index out of bound exception
});
});
希望这可以帮到你。 :)