iphone CoreDat数据库 NSFetchedResultsControler

来源:互联网 发布:牛牛软件图片 编辑:程序博客网 时间:2024/05/24 06:48


 引子:NSFetchedResultsControllerUITableView集成起来处理数据具有强大的灵活性。首先得到的好处是不需要将数据记录进行分页,不然,按照传统的做法,需要先查询出总的记录,然后再从纪录里面过滤,这样会进行两次操作,对内存消耗很大,处理不好,程序甚至可能崩溃。使用NSFetchedResultsController类不仅简单,还具有更高的性能,这个类自动帮助你记录分页的事情,得到表对应的Core Data对象也非常简单。

更重要的是,你在其他界面更新或者删除记录时,NSFetchedResultsController可以帮助你同步更新UITableView,你的UITableView和数据库同步将变得非常简单。

 NSFetchedResultsController

继承自

NSObject

遵循

NSObject (NSObject)

框架

/System/Library/Frameworks/CoreData.framework

可用性

Available in iOS 3.0 and later.

指南

Core Data Programming Guide

定义于

NSFetchedResultsController.h

相关代码

CoreDataBooks

DateSectionTitles

iPhoneCoreDataRecipes

TopSongs

  当你从Core Data 取出数据,然后以表格视图的形式展现数据的时候, fetched results控制器很有用。它是连接Core Data和表格视图的桥梁,首选的一种控制器。


  (图一)



我们知道表格视图有很多种形式, fetched results 控制器主要针对列表视图(如图1),这种视图是由很多section组成的,每个section又包含了很多row。你只需配置:entity是什么,如何排序(必须配置),预筛选项(可选)。NSFetchedResultsController就会自动帮你处理好每个section都应该显示哪些数据。

 

另外,NSFecthedResultsController还提供以下特性:

  • 监控managed object context对象的改变,报告给delegate(可选).
  • 缓冲数据,让性能有所提升,专门针对需要频繁显示的数据 (可选)。

 

总结一下,fetched results 控制器 提供下面3种模式:

1.      不追踪模式:delegate设为nil.

只提供基本的查询数据访问数据的能力。

2.      内存追踪模式:delegate有值,file cache name设为nil.

controller负责监控结果集中的数据改变,针对改变调整排序。

     3.      完全追踪模式:delegatefile cache name都有值

     controller负责监控结果集中的数据改变,针对改变调整排序。还能把计算的结果存入缓存。

 

重要:作为这个类的delegate,至少要实现一个追踪方法:controllerDidChangeController。哪怕这个方法你什么也不写为空,也要实现这个方法。

 

使用NSFetchedResultsController控制器

 

创建控制器

一般来说,你会创建一个NSFetchedResultsController实例作为table view的成员变量。初始化的时候,你提供四个参数:

1 一个fetch request.必须包含一个sort descriptor用来给结果集排序。

2 一个managed object context 控制器用这个context来执行取数据的请求。

3 一个可选的key path作为section name。控制器用key path来把结果集拆分成各个section。(传nil代表只有一个section

4 一个cache file的名字,用来缓冲数据,生成section和索引信息。

 

当你实例都创建好之后,调用performFetch: 来执行查询。

 

NSManagedObjectContext *context = <#Managed object context#>;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Configure the request's entity, and optionally its predicate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:
@"<#Sort key#>" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, 
nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];

NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
        initWithFetchRequest:fetchRequest
        managedObjectContext:context
        sectionNameKeyPath:nil
        cacheName:@"<#Cache name#>"];

[fetchRequest release];

NSError *error;
BOOL success = [controller performFetch:&error];

 

重要:你不可以用一个fetched results controller来执行几个查询,如果你非要这么做,那用deleteCacheWithName:cache file先删除,不然会冲突.

 

控制器的代理

 

如果你为fetched results控制器设置了代理,代理会收到从它的managed object context中传来改变通知。delegate会处理context中任何会影响结果集或者section的变化,results也做相应的更新。控制器会告诉delegate结果集改变了什么或者section变化了哪些。你只要覆写几个方法来更新table view就行。

 

缓存

 

如果有必要,控制器会使用缓存来避免重复地构造section或者把section中的内容进行排序的工作。可以说,你程序一运行,这个缓存也跟着建立好了。

 

当你初始化一个NSFetchedResultsController实例的时候,你一般会确定好文件的名字。(如果你不定义名字,那控制器就不使用缓存)当你创建一个控制器,它会寻找一个已存在的缓存:

 

如果控制器找不到合适的缓存,它就计算section以及其内容的显示顺序。把结果信息写入硬盘。

 

如果它能找到一个同名缓存,会先检查缓存中的数据是否合法。控制器会参照当前entity的名字,entity版本信息,排序,缓存文件更改时间来判断这个缓存是否可用。

 

如果缓存和当前信息相兼容,控制器会复用之前存储的计算结果。

如果缓存和当前信息不兼容,控制器重新计算然后更新缓存。

 

一旦section和排序的信息改变了,那缓存一定会跟着改变。

所以你如果有多个fetched result控制器,配置得都不一样,那你最好用不用得缓存名字。

 

函数 deleteCacheWithName:可以直接删除一个缓存文件

 

实现table view datasource方法

 

你可以让控制器提供相应的信息,然后实现你datasource方法。

 

 

(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    
return [[<#Fetched results controller#> sections] count];
}

- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
    
id <NSFetchedResultsSectionInfo> sectionInfo = [[<#Fetched results controller#> sections] objectAtIndex:section];
    
return [sectionInfo numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = <
#Get the cell#>;
    NSManagedObject *managedObject = [<
#Fetched results controller#> objectAtIndexPath:indexPath];
    
// Configure the cell with data from the managed object.
    
return cell;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 
    
id <NSFetchedResultsSectionInfo> sectionInfo = [[<#Fetched results controller#> sections] objectAtIndex:section];
    
return [sectionInfo name];
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    
return [<#Fetched results controller#> sectionIndexTitles];
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index{
    
return [<#Fetched results controller#> sectionForSectionIndexTitle:title atIndex:index];
}

 

响应变化

 

基本上,NSFetchedResultsController设计之初就是为了应对模型层的变化,负责告知其delegate结果变化了或者section的改变了。

 

如果你允许一个用户重新对表格进行排序,那你实现的delegate方法就必须把这个也考虑进来。

 

其实, managed object context收到processPendingChanges消息的时候,所作的变化才有所反应。因此,如果你对一个managed object的属性进行了更改,存储位置变化了,控制器的结果会跟着改变。但当前事件周期结束,记录的索引才跟着变化(processPendingChanges函数被调用)。比如,下面这段代码 就应该输出“same”:

 

NSFetchedResultsController *frc = <#A fetched results controller#>;
NSManagedObject *managedObject = <
#A managed object in frc's fetchedObjects array#>;
NSIndexPath *beforeIndexPath = [frc indexPathForObject:managedObject];
[managedObject setSortKeyAttribute:
                  <
#A new value that changes managedObject's position in frc's fetchedObjects array#>;
NSIndexPath *afterIndexPath = [frc indexPathForObject:managedObject];
if ([beforeIndexPath compare:afterIndexPath] == NSOrderedSame) {
    NSLog(
@"same");
}

 

改变查询请求

 

你不能只是简单地改变fetch request来试图改变查询结果,如果你想看到变化,你应该:

1。如果你用cache,删掉它(用deleteCacheWithName:函数)

不过一般来说,查询请求都改变了,你不太可能会用一个cache.

2。改变fetch request.

3。调用 performFetch:函数

 

对象的检查

 

如果一个managed object context 告诉fetched result 控制器 一个单独的对象是无效的,那控制器就认为这样地对象已经被删除了,并且发送适当的delegate消息。

 

有的时候可能所有的对象都是无效的(比如,当我们调用reset函数或者一个存储从持久化存储管理器中被删除的时候)NSFetchResultsController不会一个个对象去检验,也不会发送删除对象的通知。而是,你必须调用performFetch:去重置管理器并且重载table view当中的数据(reloadData)。

 

iOS 版本的问题

 

iOS 4.0以后,即使你重用一个cache,那管理器也不会报错,只是显示出错误的数据而已(这样可以削减程序启动时复杂的字符串比较,提高效率)。所以注意不要重复使用!

 

子类需要注意

 

如果你想自定义section的创建,索引的标题,那你可能会子类这个控制器。你可以覆写sectionIndexTitleForSectionName:方法。

如果你想section的名字不是大写字母开头,而是其他的,你可以覆写sectionIndexTitles

如果你想索引的名字是其他的内容,为每个已知section覆写sectionIndexTitleForSectionName:

原创粉丝点击