前言:
介绍
在Cocoa环境下,如果你想使用数据库(如sqlite),你可以使用sql语句的方式通过相关的工具类进行数据库的直接操作。当然你也可以通过别人封装之后的一些简单框架,使得你的操作更加简单(如FMDB BNRPersistence)。
Cocoa框架本身提供了CoreData这个API可方便的让开发者通过操作对象的方式在操作数据库。CoreData是一个对象图(object graph)以及持久化的管理框架。我们可以通过CoreData创对象,设置好象之间的关系,然后将其持久化(我们甚至可以使用内存数据库),或者从硬盘上将持久化后的数据加载到内存中。对象图,我们可以创建一个个的对象,并维持不同对象之间的关系,一对一,一对多等。
CoreData有大量的特性,诸如支持Redo,Undo的功能,这些很多Document based的程序中显得非常的有用。提供数据model结构变化轻量级的迁移方案。CoreData还通过Binding特性和控件的紧密结合,这样使得只需要少量的代码便可以完成强大的功能,下面是一个例子
http://www.timisted.net/blog/archive/multiple-windows-with-core-data/
存储方式
Core Data可以将数据存储为XML,二进制文件或SQLite文件。在Mac OS X 10.5 Leopard及以后的版本中,开发者也可以通过继承NSPersistentStore类以创建自定义的存储格式。每种方法都有其优缺点,例如XML的可读性,SQLite的节约空间等。
Core Data的这一方面类似于原始的Enterprise Objects Framework(EOF)系统,但EOF中开发者可以使用相对简洁的查询方式,而在Core Data中,只能使用一个语法类似SQL子集的查询语言,称为Predicate。Core Data是标准化的,可以自由的读写Xcode数据模型文件(通常是.xcdatamodel文件)。
与EOF不同,Core Data目前没有设计多用户或多线程访问模式。模型迁移通常也需要代码,若其它开发者依赖于某个数据模型,则该数据模型的设计者可能在模型发生改变时需要与新数据模型一起提供版本转换代码。
![iPhone开发之CoreData(基础篇)_第1张图片](http://img.e-com-net.com/image/info5/a745bb151aeb40f7928943fa06f5d9f7.jpg)
操作简介
Core Data由相对庞大的类继承体系组成,但开发者需要关注的接口只是其中的一个相对小的子集。
一般需要定义以下Core Data的三个必备
NSPersistentStoreCoordinator *persistentStoreCoordinator;
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
以及使用时需要用到的
NSFetchedResultsController *fetchedResultsController;
具体的使用例子比较多,后面介绍一下在使用过程中遇到的一些问题。或许可以帮到你。
分享
1. 使用Table时在3.0版本时会Crash
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
3.0版本的时候如果你在numberOfRowsInSection的回调时Crash,那么加上下面这个回调就没事了,这样在无数据的时候数组就不会越界了。Apple在3.0才第一次上CoreData,总有不小心的时候,后面的版本就处理得很好。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
2. 又Crash了,检查一下,记得在所有增删改查前已经调用过下面这几句话,并且在重置fetchedResultsController 的时候也要重新调用,所以我把它写到创建的尾部了。
NSError *error;
if (![fetchedResultsController performFetch:&error])
NSLog(@"Error performing fetch: %@", [error localizedDescription]);
3. 错误信息看不懂,用
- (void) handleError:(NSError *)error fromSource:(NSString *)sourceString
{
NDLOG(@"Unresolved error %@ at %@, %@", error, sourceString, [error userInfo]);
[DataController dumpError:error];
}
打出来的确实不是很清楚。有时候还是不准确不完整的信息。换一个吧,我们自己来。
+ (void) dumpError:(NSError *) error {
NDLOG(@"Failed to save to data store: %@", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
NDLOG(@" DetailedError: %@", [detailedError userInfo]);
}
}
else {
NDLOG(@" %@", [error userInfo]);
}
}
4. 不是每次都要fetchedResultsController的,有时候直接用NSFetchRequest更方便。
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *tagEntity = [NSEntityDescription entityForName:@"entityName" inManagedObjectContext:managedObjectContext];
[request setEntity:tagEntity];
NSError *error = nil;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
return [array count];
5. 有时候没SQL命令确实效率不怎么样。为了做一个更新数据库的操作,瞧这代码,幸亏N都是很小的值。
NSMutableArray* persistent = [NSMutableArray arrayWithArray:local];
NSMutableArray* netData = [NSMutableArray arrayWithArray:netReturn];
for(id info in persistent) {
BOOL found = NO;
for(id data in netData) {
if(找到) {
更新对象数据等操作
[netData removeObject: data];
found = YES;
break;
}
}
if(!found) {
移除不必要的数据等
}
}
for(id data in netData) {
插入新增等操作
}
最后是保存等操作
}
6. 在使用的过程中,其功能强大省去了很多的事情,但是其实有很多需要注意的东西吗,delegate的处理等等。在改变模型的时候如果没有更新方案的话会很麻烦的。效率上也是值得商榷,还有一堆高度重复的代码也是看起来不舒服的地方。还有就是感觉数据源非常适合使用Table来处理。
7. 在上面的第五点的更新操作为什么需要这样子麻烦呢,因为需要避免用户在操作一个数据库中一个已经被删除的但是已经载到内存中对象。但是这种情况并没有能够完全规避,这时候在访问对象数据前,需要及时的判断这个内存中的对象还有效吗。调用对象的isFault(NSManagedObject),进行判断,及时避免不必要的操作和崩溃。
附
1. http://blog.jjgod.org/2010/02/28/core-data-or-not/ 是否该用 Core Data。在IOS上使用数据库的一点不同的声音。
iPhone开发之CoreData(基础篇)
4人收藏此文章, 我要收藏我要收藏发表于6个月前(2012-07-29 10:56) , 已有
2205次阅读 ,共
0个评论
找有没有SQLite的封装类,这样可以避免自己重复地SQLite的封装代码,Google一下,找 到几个objc-sqlite、FMDB等,还有一些其它不知名的封装库,官方也提供了CoreData框架来提供完整的对象持久化存储方案,还是有必要 要学习一下。
基本概念
在CoreData有一些概念刚学习的时候不是很容易理解,还是要单独拿出来来梳理一下,这样学后面的内容不会感觉吃力。
新建一个项目,项目模板基于“Master-Detail Application”,点击“Next”按钮,项目命名为“SimpleCoreData”,并勾选“Use Core Data”,点击“Next”,选择项目保存的目录,点击“Create”按钮,项目创建完毕。
代码分析
比以前创建的简单项目多了不少代码,还有xcdatamodeld文件,慢慢分析代码,AppDelegate.h头文件中,添加了三个property
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
再分析AppDelegate.m文件,有对应的三个方法来返回各自对应的对象
#pragma mark - Core Data stack - (NSManagedObjectContext *)managedObjectContext{ ...
return __managedObjectContext;} - (NSManagedObjectModel *)managedObjectModel{ ...
return __managedObjectModel;} - (NSPersistentStoreCoordinator *)persistentStoreCoordinator{ ...
return __persistentStoreCoordinator;}
这些对象在哪里被调用的呢,打开MasterViewController.m,在初始化函数中,我们看到通过获取delegate,再通过 delegate调用方法managedObjectContext,这样就得到了这个NSManagedObjectContext对 象,NSManagedObjectContext对象它会跟NSPersistentStoreCoordinator对象打交 道,NSPersistentStoreCoordinator会去处理底层的存储方式。
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) { self.title = NSLocalizedString(@"Master", @"Master");
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
} return self;}
查询实体
分析MasterViewController.m的代码发现以下函数的调用顺序。
- ♥ -tableView:(UITableView *)tableView cellForRowAtIndexPath:
- ♥ -configureCell:atIndexPath:
- ♥ -fetchedResultsController
最后是从fetchedResultsController获取到查询结果,那就有必要来分析一下
- (NSFetchedResultsController *)fetchedResultsController{ // 如果查询结果已经存在就直接返回__fetchedResultsController if (__fetchedResultsController != nil) { return __fetchedResultsController;
}
// 1.创建NSFetchRequest对象(相当于SQL语句) NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// 2.创建查询实体(相当于设置查询哪个表) NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// 设置获取数据的批数. [fetchRequest setFetchBatchSize:20];
// 3.创建排序描述符,(ascending:是否升序) NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// 根据fetchRequest和managedObjectContext来创建aFetchedResultsController对象,并设置缓存名字为"Master". NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
// 设置aFetchedResultsController的委托对象为当前类 aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
// 获取第一批数据 if (![self.fetchedResultsController performFetch:&error]) { // 错误处理 NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return __fetchedResultsController;}
因为我们设置了aFetchedResultsController的委托NSFetchedResultsControllerDelegate,就要实现它的方法,幸运的是这些方法看起来都像下面这样,直接复制粘贴就行了。
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller{ [self.tableView beginUpdates];} - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)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)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];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
} } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{ [self.tableView endUpdates];}
添加实体
- (void)insertNewObject{ // 从NSFetchedResultsController中获取NSManagedObjectContext对象 NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
// 从NSFetchedResultsController中获取实体描述 NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
// 在被管理上下文中插入一个新的NSManagedObject NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// 字段赋值 [newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];
// 保存被管理对象上下文,这样刚才的newManagedObject就被保存了 NSError *error = nil;
if (![context save:&error]) { // 错误处理. NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
} }
删除实体
// 从NSFetchedResultsController中获取NSManagedObjectContext对象 NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
// 从被管理对象上下文中删除MO对象 [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
// 保存被管理对象上下文 NSError *error = nil;if (![context save:&error]) { // 错误处理 NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();}
这个删除对象为什么没有处理TableView的代码,因为我们实现NSFetchedResultsControllerDelegate委托,当对象上下文发生变化时NSFetchedResultsControllerDelegate的实现方法会处理这些事情。