在UITableView上执行GCD
当我创build单元格时,我正在进行一些繁重的计算。 我试图找出保持UITableViewstream体的最佳方式,但是在相同types的背景上进行计算(保持UI线程没有太多的处理)。
仅用于testing目的,我使用这个作为我繁重的计算方法:
+(NSString*)bigCalculation { int finalValue=0; int j=0; int i=0; for (i=0; i<1000; i++) { for (j=0; j<10000000; j++) { j++; } finalValue+=j/100*i; } return [NSString stringWithFormat:@"%d",finalValue]; }
在cellForRowAtIndexPath里面,我只做下面的事情:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *identifier=@"identifier"; UITableViewCell *cell=nil; cell=[aTableView dequeueReusableCellWithIdentifier:identifier]; if(!cell) { cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier]; } NSString *text=[dataSource objectForKey:[[dataSource allKeys] objectAtIndex:indexPath.row]]; dispatch_queue_t a_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); ; dispatch_async(a_queue, ^{ NSString *subtitle=[CalculationEngine bigCalculation]; dispatch_async(dispatch_get_main_queue(), ^{ [[cell detailTextLabel] setText:subtitle]; dispatch_release(a_queue); }); }); [[cell textLabel] setText:text]; return cell; }
目前我有UITableViewstream体,而在后台,一切工作正常。 所以我的问题是:
1)这是达到我想要的最好的方式,KVO也可以作为答案吗?
2)在这之前:
dispatch_queue_t a_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
我在做:
dispatch_queue_create("com.mydomain.app.newimagesinbackground", DISPATCH_QUEUE_SERIAL)
而且performance很差。 你能解释我为什么吗?
这不是一个好方法。 通过将计算工作推入视图控制器,您打破了MVC。 您应该将数据保存在模型对象中并在那里pipe理计算。 表视图应该只显示模型中的当前值,使用reloadRowsAtIndexPaths:withRowAnimation:
当数据改变时。 KVO是确定数据何时更改的一种合理方法。
如果你想在计算中懒惰,那么你可以调用recalculateIfNeeded
模型,让模型知道某人(在这种情况下,表视图)会有一个新的值。 但是,如果input数据没有实际改变,则不应该重新计算。 该模型是在哪里跟踪数据是否已经改变。
首先
你的实现从根本上是有缺陷的。 您将对单元格的引用放入在后台执行的块中。 当你滚动和单元格被取消屏幕时,他们被放入一个重用池。 随着新的细胞进入屏幕,他们被从这个池中拉出。 当您的块完成时,单元格可能不再位于同一行中。 为了说明这一点,我把这个声明放在工作块的开始处:
NSLog(@"my Row is: %d myCell is: %x", indexPath.row, (unsigned int)cell);
这导致:
我的行是:0 myCell是:16fd20
我的行是:13 myCell是:16fd20
我的行是:24 myCell是:16fd20
我的行是:35 myCell是:16fd20
我的行是:44 myCell是:16fd20
我的行是:56 myCell是:16fd20
我的行是:66 myCell是:16fd20
所以在这里你可以看到7块,全部计算不同的行,但都指向相同的单元格。
如果用户在块完成时滚动,错误的单元格将被更新。
当您更新单元以确保立即处理时dispatch_sync(dispatch_get_main_queue(),0)
您还希望使用dispatch_sync(dispatch_get_main_queue(),0)
。
KVO是解决这个问题的一种方法,导致..
你的第一个问题:
KVO将是一个很好的方法来做到这一点。 正如Rob Napier所说,您应该为列表中的每个项目分别设置一个模型对象。 为了pipe理tableView中的单元格的更新,你想要inheritanceUITableViewCell。 然后,单元格为模型对象预订通知,并且在进入时可以自行更新。如果单元模型对象发生更改,则只需为旧模型对象退出通知并订阅新模型对象。
这应该保证你不会在当前代码中显示不准确的信息。
需要注意的是:KVO会在设置新值的同一个线程上发送通知。 这意味着您需要确保您在主线程上设置新值,或者在您的observeValueForKeyPath:
方法的主线程上分派块。
你的第二个问题:
这样得到队列的原因是:
dispatch_queue_create("com.mydomain.app.newimagesinbackground", DISPATCH_QUEUE_SERIAL);
是如此之慢,你是每个块计划生成一个串行队列。 这些队列将全部同时执行。 由于你只有less量的内核,因此队列的执行时间越来越less。主队列(运行UI)只是另一个队列,执行的时间也更短,小。
在我的testing中,我发现每个队列都有一个线程。 与细胞一样多的线程。
使用全局并发队列:
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
允许系统一次pipe理要执行的块的数量。 这样做的最终结果是,而不是数百个队列和数百个线程,你有一个队列,并在我的iPhone 4s,两个线程。 每个核心一个线程。 这使调度更简单,并为线程分配更准确的优先级。 最终的结果是主线程有足够的时间执行。