测试已测试 | ✓ |
语言语言 | Obj-CObjective C |
许可协议 | MIT |
发布日期最后发布日期 | 2015 年 10 月 |
由 Konstantin Pavlikhin 维护。
专为桌面 Cocoa 重新实施的最高级的 NSFetchedResultsController
。
假设您遇到一个问题并希望使用 CoreData。恭喜,现在您有两个问题
CoreData 于 10.4 Tiger(2005 年)推出后,多年来开发人员一直在使用这两个中介控制器之一构建 AppKit 应用程序。
确实可以使用 NSArrayController
与 CoreData(如果您的交互性需求相对简单)。
NSTreeController
完全无效,应该被活活烧死。
我对 NSTreeController
的个人抱怨如下
NSOutlineView
更改管理NSPredicate
的选项我多次尝试使用这种毫无意义的东西,都失败了。
在 OS X 10.7 狮子中,NSTableView/NSOutlineView
类经过了彻底的重构。它们开始允许基于 NSView
的单元格,而不是丑陋且极不方便的基于 NSCell
的单元格。第二大重要变化是能够对表示的数据集进行增量更改(插入行/移动行/删除行/更新行)并动画相应的转换。所有这些更改使 NSTableView/NSOutlineView
对的行为和外观更像 UIKit 中的 UITableView
。不幸的是,现有的控制器对象(NSArrayController
和 NSTreeController
)并未更新以利用最新的增强功能。如果模型发生变化,NSObjectController
后代将简单通过 -reloadData
方法重新加载整个表。对我来说,这感觉像一个无礼、冗余且不优雅的解决方案,可能具有较差的性能(如果您有一个具有可变行高的表,则表将丢失缓存并重新查询每行的高度,即使是不可见的行)。使用新表格视图功能的方法是丢弃这些粘合对象并以手动方式处理表格。
由于某种原因,苹果决定不将 NSFetchedResultsController
移植到 OS X 上。鉴于苹果一直以来的坏习惯,即使是再荒谬的bug也不会修复,所以这个缺乏获取结果控制器的微小问题并不让我感到惊讶。
原始的 NSFetchedResultsController
是为了在 iOS 上运行并嵌入到 UITableView
实例中设计的。鉴于 UITableView
的 API 与 AppKit 的 NSTableView
的差异,我们不能简单地复制粘贴类接口并编写 FRC 实现。无法创建一个有用的即插即用组件。相反,我们必须移植基本思想,并将其集成到现有的 AppKit 基础设施中。
NSTableView
和 UITableView
之间最显著的不同之处在于后者有一个表分区概念。UITableView
会“请求”其代理返回特定索引路径上的对象。而 NSTableView
最多只能显示所谓的分组行,但底层模型仍然必须是平的(NSArray
)。幸运的是,我们可以使用支持任意项嵌套的 NSOutlineView
来模拟分区。NSOutlineView
不会操作索引路径,而是使用一个概念为项,这些项可以成为其他项的子项。这意味着我们需要一种特殊类型的项来表示大纲视图中的分区。这就是 KSPTableSection
的用途。
那么,本质是什么呢?
NSTableView
,另一个带有分区支持,使用层次结构存储库的 NSOutlineView
。NSFetchedResultsController
的索引路径概念,因为 NSOutlineView
使用完全不同的 API(-numberOfChildrenOfItem:/-child:ofItem:
)。这个类是为了使用 NSTableView
数据源而设计的。
您可以使用一个获取请求和一个托管对象上下文来实例化一个 KSPFetchedResultsController
实例。在需要执行获取时,FRC 的 fetchedObjects
属性会填充 NSManagedObject
。获取结果控制器会监听上下文更改通知,并以集合-KVO 兼容的方式更新其 fetchedObjects
集合。为了向 NSTableView
发出粒度更新的请求,您必须成为获取结果控制器的代理,并在您的控制器对象(很有可能是自定义的 NSViewController
子类)中实现 KSPFetchedResultsControllerDelegate
协议。
这通常看起来是这样的
#pragma mark - KSPFetchedResultsControllerDelegate Implementation
- (void) controllerWillChangeContent: (KSPFetchedResultsController*) controller
{
[self.tableView beginUpdates];
}
- (void) controller: (KSPFetchedResultsController*) controller didChangeObject: (NSManagedObject*) anObject atIndex: (NSUInteger) index forChangeType: (KSPFetchedResultsChangeType) type newIndex: (NSUInteger) newIndex
{
switch(type)
{
case KSPFetchedResultsChangeInsert:
{
[self.tableView insertRowsAtIndexes: [NSIndexSet indexSetWithIndex: newIndex] withAnimation: NSTableViewAnimationEffectNone];
break;
}
case KSPFetchedResultsChangeDelete:
{
[self.tableView removeRowsAtIndexes: [NSIndexSet indexSetWithIndex: index] withAnimation: NSTableViewAnimationEffectNone];
break;
}
case KSPFetchedResultsChangeMove:
{
[self.tableView moveRowAtIndex: index toIndex: newIndex];
break;
}
case KSPFetchedResultsChangeUpdate:
{
{{
NSIndexSet* rowIndexes = [NSIndexSet indexSetWithIndex: index];
NSIndexSet* columnIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, self.tableView.tableColumns.count)];
[self.tableView reloadDataForRowIndexes: rowIndexes columnIndexes: columnIndexes];
}}
break;
}
}
}
- (void) controllerDidChangeContent: (KSPFetchedResultsController*) controller
{
[self.tableView endUpdates];
}
这个类是为了使用 NSOutlineView
数据源而设计的。
KSPSectionedFetchedResultsController
继承自 KSPFetchedResultsController
并向后者添加了分区管理功能。
就像使用KSPFetchedResultsController
一样,您需要使用一个fetch请求和一个管理对象上下文实例化一个KSPSectionedFetchedResultsController
实例。另外,您还需要提供用于将对象分组到组的分区名称键路径。当您进行一次fetch操作时,SFRC的sections
属性会填充KSPTableSection
实例。获取结果控制器会监听上下文的更改通知,并以KVO兼容的方式更新其sections
集合。要向NSOutlineView
发出细粒度更新,您必须成为分区获取结果控制器的代理,并在您的控制器对象中实现KSPSectionedFetchedResultsControllerDelegate
协议(最可能的是一个自定义的NSViewController
子类)。
这通常看起来是这样的
#pragma mark - KPSectionedFetchedResultsControllerDelegate Implementation
- (void) controllerWillChangeContent: (KSPFetchedResultsController*) controller
{
[self.outlineView beginUpdates];
}
- (void) controller: (KSPSectionedFetchedResultsController*) controller didChangeObject: (NSManagedObject*) anObject atIndex: (NSUInteger) index inSection: (KSPTableSection*) section forChangeType: (KSPFetchedResultsChangeType) type newIndex: (NSUInteger) newIndex inSection: (KSPTableSection*) newSection
{
switch(type)
{
case KSPFetchedResultsChangeInsert:
{
[self.outlineView insertItemsAtIndexes: [NSIndexSet indexSetWithIndex: newIndex] inParent: newSection withAnimation: NSTableViewAnimationEffectNone];
break;
}
case KSPFetchedResultsChangeDelete:
{
[self.outlineView removeItemsAtIndexes: [NSIndexSet indexSetWithIndex: index] inParent: section withAnimation: NSTableViewAnimationEffectFade];
break;
}
case KSPFetchedResultsChangeMove:
{
[self.outlineView moveItemAtIndex: index inParent: section toIndex: newIndex inParent: newSection];
break;
}
case KSPFetchedResultsChangeUpdate:
{
[self.outlineView reloadItem: anObject reloadChildren: NO];
break;
}
}
}
- (void) controller: (KSPSectionedFetchedResultsController*) controller didChangeSection: (KSPTableSection*) section atIndex: (NSUInteger) index forChangeType: (KSPSectionedFetchedResultsChangeType) type newIndex: (NSUInteger) newIndex
{
switch(type)
{
case KSPSectionedFetchedResultsChangeInsert:
{
[self.outlineView insertItemsAtIndexes: [NSIndexSet indexSetWithIndex: newIndex] inParent: nil withAnimation: NSTableViewAnimationEffectNone];
break;
}
case KSPSectionedFetchedResultsChangeDelete:
{
[self.outlineView removeItemsAtIndexes: [NSIndexSet indexSetWithIndex: index] inParent: nil withAnimation: NSTableViewAnimationEffectNone];
break;
}
case KSPSectionedFetchedResultsChangeMove:
{
[self.outlineView moveItemAtIndex: index inParent: nil toIndex: newIndex inParent: nil];
break;
}
}
}
- (void) controllerDidChangeContent: (KSPFetchedResultsController*) controller
{
[self.outlineView endUpdates];
}
这个类是为了使用 NSOutlineView
数据源而设计的。
KSPMirroredSectionedFetchedResultsController
继承自KSPSectionedFetchedResultsController
。它唯一的责任是反转嵌套对象的排序顺序。在某些情况下,比如在您的NSOutlineView
中实现“下拉加载更多”功能时,这可能会更高效、更有逻辑。
KSPFetchedResultsController
没有缓存概念,这在NSFetchedResultsController
中是存在的。
KSPFetchedResultsController
也没有在NSFetchedResultsController
中实现的操作模式概念。
NSFetchedResultsController
有一个长期的缺陷(也可能是特性?),当你修改NSFetchRequest
的fetchLimit
和fetchOffset
属性时会出现这个缺陷。例如,当你请求CoreData存储按照特定标准排序并带有偏移量10和限制10的对象时,你最初得到正确的结果。但是,在新的NSManagedObject
被插入上下文并通过谓词后,它会被立即报告给FRC的代理作为已插入的对象,尽管它可能实际上不属于跳过前10个对象并排除后面10个的“窗口”。
KSPFetchedResultsController
遵循这种行为以与iOS FRC版本兼容。
就是这些!告诉我您对此的看法。