设置:我有一个由简单数组数据源支持的UICollectionView
或UITableView
。我在控制器中保留了该数据源的副本。
现在,我从系统收到一条通知,告知有新数据可用。我得到一个新的数组,其中可能已添加,删除和更改位置。
所以现在我有两个数据对象:
为了让UI与新数组同步,我需要生成一堆UI调用。如果是UICollectionView
,则为
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths
UITableView
有一套类似的方法。
我特别不想重新加载整个表格,因为这比仅使用几个项目更贵。
所以,问题是:给定前一个和新的数据源数组,如何生成正确的UI调用集,以及何时“换出”新的旧数据源?
答案 0 :(得分:8)
我认为这在很大程度上等同于 diff / patch 问题,其目的是找到一个文本文件与另一个文本文件之间的最小更改次数。然后应用这些更改。在这种情况下,实现定义了操作:
...但不是移动。省略 move 的原因对我来说并不是很明显,但我强烈怀疑包含 move 会需要非常昂贵的计算来找到最佳运动。
因此,如果我们将操作限制为上面列出的那些操作,An Algorithm for Differential File Comparison中描述的Hunt-McIlroy算法或其descendents中的一个算法将找到接近最佳的更改集。
您的问题与经典 diff / 补丁之间的区别在于您有一个二维表,而 diff / patch 处理一维项目(文本行)。将二维问题转换为一维问题的最佳方法取决于数据表中倾向于进行的更改的特定特征。
例如,如果表是 n 行 m 列,并且更改往往按行分组或行插入或删除整体,那么你会可能最好将表视为文本文件,并逐行执行 diff 。或者,如果更改倾向于按列分组或插入或删除列,则可以按列执行 diff 列。如果更改包括插入或删除单个单元格(导致后续单元格因此向右或向左移动),则可以将表格视为表格中的每个单元格位于文本文件的单独行上,从而使表格线性化以行优先或列优先顺序排列。
然而,在不知道问题的细节的情况下,我倾向于避免过早优化。因此,如果 m < m ,那么我倾向于从逐行实施Hunt-McIlroy算法开始。如果 n < n ,或逐列。 m ,然后在使用一段时间之前对应用程序进行概要分析,然后再决定是否需要更复杂的算法版本或者将问题替换为Hunt-McIlroy解决方案。
可以找到各种 diff 算法的详细讨论here on stackoverflow。
答案 1 :(得分:4)
我已经开发了一个应用程序来处理我能够解决的非常类似的问题。我期待一个复杂的解决方案,但它非常简单。以下是解决问题的方法:
1)创建一个新数组,在其中您将收到新项目并调用它让我们说'moreItems',当然也可以合成它。
@property (nonatomic, readonly) NSMutableArray *moreItems;
2)在与TableView或CollectionView链接的ViewController的viewDidLoad中,分配/ init这个数组:
moreItems = [[NSMutableArray alloc] init];
3)现在你需要添加你现有的数组,让我们说它是你刚刚创建的名为'moreItems'的新数组的“项目”。您可以使用'addObjectsFromArray'
执行此操作[moreItems addObjectsFromArray:[channel items]];
'channel items'包含之前收到的对象,它会将这些项添加到名为'moreItems'的新创建的数组中。我假设您正在从某些Web服务收集数据,因此您可以在connectionDidFinsihLoading中实现此步骤。
4)现在更改TableView / CollectionView的数据源,并将其替换为“moreItems”的新数组
5)下一个问题是你不想重新加载整个tableview并想要处理新项目。要拥有此功能,您需要保留项目。无论您喜欢什么,都可以使用归档或核心数据。
6)已经提取的项目,你必须坚持下去,让我们说存档。当用户打开应用程序时,在桌面视图中显示它们,同时抓取更多在Web服务上更新的项目。首先立即显示持久化项目,然后处理新项目。
7)你需要寻找一些独特的对象,在我的例子中它是'链接',因为每个项目都有不同的链接,我可以在此基础上对它们进行排序和处理。您还需要使用
- (BOOL) isEqual:(id)object
比较Web服务上的链接和tableview中已有的链接。此步骤是必要的,因为您的应用程序不会添加包含已经在tableview中的链接的项目。
8)如果每个项目都有一些日期,您可以使用该日期对它们进行排序,并使用'sortUsingComparator'
在顶部显示新日期9)此外,您还需要使用“[[table tableView] insertRowsAtIndexPath:rows withRowAnimation:UITableViewRowAnimationTop”来显示该效果,向用户显示新项目已添加到之前的项目之上。
希望这有帮助。
答案 2 :(得分:3)
我特别不想重新加载整个表格,因为那样 比只使用一些物品更贵。
你确定这是真的吗?除非你有一个病态的案例,屏幕上可以看到每个单元格,并且有数百个单元格,我发现很难相信“有效”的方式如何比“蛮力”方式更快。</ p>
调用reloadData
只会让您的代表更新那些最终会在屏幕上显示的单元格,它将不导致整个表格视图重新创建其每个单元格。
如果您正在实施某些方法来改变计算维度的复杂性(例如tableView:heightForRowAtIndexPath
),那么使用“高效”方式将无法获得任何好处,因为表格视图无论如何都必须重新计算其所有高度。
我不确定你所寻求的解决方案是否能解决这个问题,但我们只是希望我非常错误。
答案 3 :(得分:2)
如果您的数据源已有差异信息,UITableView reloadRowsAtIndexPaths:withRowAnimation:会降低性能: