钢丝绳电动葫芦参数:corea data学习—3

来源:百度文库 编辑:九乡新闻网 时间:2024/05/02 07:35:09

这是系列教程的第三部也是最后一部,有助于你快速掌握Core Data基础。

系列教程一中,我们为对象建立了可视化数据模型,运行了快速肮脏测试并勾在一个表视图(table view)中来显示。而在这个教程,我们将讨论如何把已有的数据导入或者预先载入到Core Data里面,这样我们的应用开始时会有一些好的默认数据。

系列教程二中, 我们讨论了如何把已有的数据导入或者预先载入到Core Data里面,这样我们的应用开始时会有一些好的默认数据。

在本教程,我们将讨论如何使用NSFetchedResultsController来优化应用,降低内存开销和提升响应时间。

为什么要采用NSFetchedResultsController


迄今为止,我们现在的处境跟当初用SQLite3的时候一样。然而,我们不需要写如此多的代码(注意一下FailedBankDatabase类中那段缺失的原始SQL语句代码),添加诸如插入/删除之类的操作也很简便。

有一项显著的便利性是用Core Data才能体现出来的:使用NSFetchedResultsController。

比较理想的状态是在用户目前所见的table view中加载部分行。幸运的是,苹果为我们造就了这一便利,提供了一个很棒的工具类:NSFetchedResultsController。

因此,开始打开FailedBanksListViewController.h,移除旧有的NSArray failedBankInfos,替换为一个新的NSFetchedResultsController:

@interface FailedBanksListViewController : UITableViewController             {            NSFetchedResultsController *_fetchedResultsController;            NSManagedObjectContext *_context;            }            @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;            @property (nonatomic, retain) NSManagedObjectContext *context;            @end

在synthesize段移除老的failedBankInfos代码并添加:

@synthesize fetchedResultsController = _fetchedResultsController;

以防忘记,在dealloc方法中fetchedResultsController设为nil

self.fetchedResultsController = nil;

另一个绝妙的地方就是你可以在viewDidUnload里面将NSFetchedResultsController设为nil,这就意味着在低内存情况下,内存的所有数据都可以释放掉(在视图离开视线的情况下)。你只需要在viewDidUnload里面把它清空(并保证在viewDidLaod中重新初始化)

- (void)viewDidUnload {            self.fetchedResultsController = nil;            }

好的,现在进到有趣的部分—创建我们的fetch results controller!我们要为属性重载get方法以检查该控制器是否存在。如果存在,返回它,否则我们将新建一个。

 

在文件的顶端添加以下函数:

- (NSFetchedResultsController *)fetchedResultsController {            if (_fetchedResultsController != nil) {            return _fetchedResultsController;            }            NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];            NSEntityDescription *entity = [NSEntityDescription            entityForName:@"FailedBankInfo" inManagedObjectContext:_context];            [fetchRequest setEntity:entity];            NSSortDescriptor *sort = [[NSSortDescriptor alloc]            initWithKey:@"details.closeDate" ascending:NO];            [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];            [fetchRequest setFetchBatchSize:20];            NSFetchedResultsController *theFetchedResultsController =            [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest            managedObjectContext:_context sectionNameKeyPath:nil            cacheName:@"Root"];            self.fetchedResultsController = theFetchedResultsController;            _fetchedResultsController.delegate = self;            [fetchRequest release];            [theFetchedResultsController release];            return _fetchedResultsController;            }

这可能看起来跟以前在viewDidLoad里面写的代码很像,就在拉取FailedBankInfo对象建立获取请求的时候。然而,这里有一些新的东东,让我们看看…

首先,无论何时用到NSFetchedResultsController,我们需要为获取请求设定一个排序描述符。这一描述符恰好为我们所需的结果排好了顺序。

排序描述符最酷的地方就是它很强大。你不仅可以为你返回的对象排序,还可以为相关的对象排序—正如你现在所看到的!我们需要在FailedBankDetails中依照close date来排序,但只用在FailedBankInfo中接收数据—Core Data能办到!

下一条语句非常重要—设置获取到的请求数据的分批大小。事实上,这正是我们需要用到fetched results controller的地方。这样,fetched results controller只会从基础数据库中获取对象的子集,并在滚动的时候获取更多的数据。

所以一旦我们设置好了排序描述符和分批大小,我们就建好了一个NSFetchedRequestController。接着将其传递给获取请求。注意到还有一些其他参数:

  • 对于管理对象上下文,我们只需传递我们的上下文。
  • 在表视图中,小节名称关键路径(section name key path)使得我们可以将数据按节排序。如果需要,我们也可以按照State来排序,例如此处。
  • cacheName是fetched results controller用于缓存一些重复工作,比如小节设置、内容排序什么的。

所以我们现在有了一个方法来返回fetched results controller,让我们修改本类做到不用老的数组方法。首先,更新viewDidLoad为:

- (void)viewDidLoad {            [super viewDidLoad];            NSError *error;            if (![[self fetchedResultsController] performFetch:&error]) {            // Update to handle the error appropriately.            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);            exit(-1);  // Fail            }            self.title = @"Failed Banks";            }

以上所做是为了取得fetchedResultsController的句柄(同时也隐式创建了它),以及调用performFetch取得第一批数据。

然后,更新numberOfRowsInSection:

- (NSInteger)tableView:(UITableView *)tableView            numberOfRowsInSection:(NSInteger)section {            id  sectionInfo =            [[_fetchedResultsController sections] objectAtIndex:section];            return [sectionInfo numberOfObjects];            }

同样更新cellForRowAtIndexPath为:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {            FailedBankInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];            cell.textLabel.text = info.name;            cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@",            info.city, info.state];            }            - (UITableViewCell *)tableView:(UITableView *)tableView            cellForRowAtIndexPath:(NSIndexPath *)indexPath {            static NSString *CellIdentifier = @"Cell";            UITableViewCell *cell =            [tableView dequeueReusableCellWithIdentifier:CellIdentifier];            if (cell == nil) {            cell = [[[UITableViewCell alloc]            initWithStyle:UITableViewCellStyleSubtitle            reuseIdentifier:CellIdentifier] autorelease];            }            // Set up the cell...            [self configureCell:cell atIndexPath:indexPath];            return cell;            }

注意到我们把逻辑部分单独分离到configureCell方法中—这是因为我们接下来还会用到。

还有一件事就是—我们需要为NSFetchedResultsController实现委托方法。好消息是这些都很模板化—我从苹果的例程中拷了出来。所以我们只要在文件底部增加一些方法:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {            // The fetch controller is about to start sending change notifications, so prepare the table view for updates.            [self.tableView beginUpdates];            }            - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {            UITableView *tableView = self.tableView;            switch(type) {            case NSFetchedResultsChangeInsert:            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];            break;            case NSFetchedResultsChangeDelete:            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];            break;            case NSFetchedResultsChangeUpdate:            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];            break;            case NSFetchedResultsChangeMove:            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];            // Reloading the section inserts a new row and ensures that titles are updated appropriately.            [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];            break;            }            }            - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {            switch(type) {            case NSFetchedResultsChangeInsert:            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];            break;            case NSFetchedResultsChangeDelete:            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];            break;            }            }            - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {            // The fetch controller has sent all current change notifications, so tell the table view to process all updates.            [self.tableView endUpdates];            }

编译运行你的工程,看起来是跟以前一样。然而,如果你看看调试输出,你会看到一些惊人的东西…

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 SELECT 0, t0.Z_PK FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK ORDER BY t1.ZCLOSEDATE DESC total fetch execution time: 0.0033s for 234 rows. SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZSTATE, t0.ZCITY, t0.ZDETAILS FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK WHERE t0.Z_PK IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY t1.ZCLOSEDATE DESC LIMIT 20 total fetch execution time: 0.0022s for 20 rows. SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZSTATE, t0.ZCITY, t0.ZDETAILS FROM ZFAILEDBANKINFO t0 LEFT OUTER JOIN ZFAILEDBANKDETAILS t1 ON t0.ZDETAILS = t1.Z_PK WHERE t0.Z_PK IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY t1.ZCLOSEDATE DESC LIMIT 20 total fetch execution time: 0.0017s for 20 rows.

你可以看到在现在这种情况下,NSFetchedResultsController获取到了一列以一定顺序排列的FailedBankInfo ID。然后,随着用户翻页而加载一批不同的内容—而不是一次全部载入到内存中

把代码给我!


这是教程迄今为止的完整示例工程。

下一步要做什么?


此刻你应该对Core Data基础有了一个很好的认识。照SQLite教程所做的那样增加细节视图或是添加 新增/编辑/删除项是一个不错的练习材料。

我同样建议好好看看苹果的教程—那里有5个不同的Core Data例子,表现出了各种不同而有趣的方方面面供你使用。