钢丝绳电动葫芦参数: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 { idsectionInfo = [[_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 18SELECT
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例子,表现出了各种不同而有趣的方方面面供你使用。