在使用core data的时候难免粗心出些状况。遇到Fetch Request must have an entity错误的时候,无非是排查2部分。
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"TextField" inManagedObjectContext:context];
首先确认context非空,接着确认实体名称和先前建立的是否相同。一般就是这两处会导致报错。我排除了很久,才发现把Textfield拼写成TextFiled了~~~结果就报错了
下面转载自:http://blog.csdn.net/ajrm0925/article/details/7410978
在工程中引入core data的步骤:
第一步:创建一个 DataDemo.xcdatamodel 文件,在其中建一个实体名字为 Entity1 ,实体中建两个字段 id 和 name。
第二步:为项目添加 CoreData.framework 框架,然后在 .pch 中加入 #import <coredata/coredata.h>
第三步:在 app delegate 中定义 core data 的相关对象(同时完成它们的初始化工作)
.h 文件:
#import <coredata/coredata.h> //貌似不是必须的,因为上面第二步已经加了,那个有全局意义
@private
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSString*) applicationDocumentsDirectory;
- (void) saveContext;
.m 文件:
@synthesize managedObjectModel;
@synthesize managedObjectContext;
@synthesize persistentStoreCoordinator;
- (void)dealloc {
[managedObjectModel release];
[managedObjectContext release];
[persistentStoreCoordinator release];
[super dealloc];
}
- (NSString*)applicationDocumentsDirectory
{
//return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
//return [NSURL URLWithString:basePath];
}
- (void)saveContext{
NSError *error = nil;
NSManagedObjectContext *objectContext = self.managedObjectContext;
if (objectContext != nil)
{
if ([objectContext hasChanges] && ![objectContext save:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
- (NSManagedObjectContext *)managedObjectContext
{
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel
{
if (managedObjectModel != nil) {
return managedObjectModel;
}
//从本地所有 xcdatamodel 文件中获得这个 CoreData 的数据模板
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
//数据库名为TestDB.sqlite
NSURL *storeUrl = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"TestDB.sqlite"]];
NSError *error;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nilURL:storeUrl options:nil error:&error]) {
NSAssert(0, @"persistentStoreCoordinator init failed!");
}
return persistentStoreCoordinator;
}
- (void)applicationWillTerminate:(UIApplication *)application
{
NSError *error;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
NSAssert(0, @"save changes failed when terminage application!");
}
}
}
第四步:在相关的 viewcontroller 中操作数据,基本的 core data 操作代码如下:
NSError *error;
testSQLiteAppDelegate *app = (testSQLiteAppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = app.managedObjectContext;
//1、插入
NSManagedObject *newManagedObject = nil;
newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"Entity1" inManagedObjectContext:context];
[newManagedObject setValue:[NSNumber numberWithInt:1] forKey:@"id"];
[newManagedObject setValue:@"i love you" forKey:@"name"];
if (![context save:&error]){
NSLog (@"The error is: %@", [error userInfo]);
}
else
{
NSLog (@"The newManagedObject's id is: %@", [newManagedObject valueForKey:@"id"]);
}
//2、查询
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Entity1" inManagedObjectContext:context];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"id" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSArray *objects = [context executeFetchRequest: fetchRequest error:&error];
if (objects == nil)
{
NSLog(@"There was an error!");
}
else {
for (NSManagedObject *oneObject in objects)
{
NSLog(@"%@",[oneObject valueForKey:@"name"]);
}
}
//3、删除
[context deleteObject:[objects objectAtIndex:0]];
if (![context save:&error]) {
exit(-1);
}
这里更详细的补充一点关于 core data 的操作代码:
1、条件过滤:选择 cid=1 的数据
- (NSFetchedResultsController*) fetchedResultsController
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"cid=1"];
[fetchRequest setPredicate:predicate];
}
2、通用代码,在 viewcontroller 里面会用到:
TestViewController.h 的代码:
@interface TestViewController : UITableViewController <NSFetchedResultsControllerDelegate>
{
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
}
@property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
- (void)configureCell:(UITableViewCell *)cell withCategory:(Category *)category;
@end
TestViewController.m 的代码:
-(NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController != nil)
{
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *arraySortDescriptor = [[NSArray alloc] initWithObjects:nameDescriptor, nil];
[fetchRequest setSortDescriptors:arraySortDescriptor];
NSPredicate *predicate = [NSPredicate predicateWithFormat:cidString];
[fetchRequest setPredicate:predicate];
NSFetchedResultsController *frController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"name" cacheName:@"Root"];
frController.delegate = self;
self.fetchedResultsController = frController;
[frController release];
[fetchRequest release];
[nameDescriptor release];
return fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)
{
[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 NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] withCategory:anObject];
break;
default:
break;
}
}
- (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) controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
总结下:
大致的意思就是先根据.xcdatamodeld建立一个managedObjectModel, 参看1.加注: k, n5 C3 M& a$ U5 B* {
9 y) G1 y2 s" e
然后根据 managedObjectModel 建立 persistentStoreCoordinator ,并且利用persistentStoreCoordinator建立用于存储的数据库文件.sqlite的映射Persistent Store。
然后根据 persistentStoreCoordinator 建立 managedObjectContext。managedObjectContext从Persistent Store中获取对象,managedObjectContex操纵这些对象的临时副本,然后便可以任意修改这些对象。除非保存这些修改,否则持久化存储是不会受影响的。
-(void)saveContext这个方法是在所需的三个之外的,它可能是用于处理在保存context时的错误的。
区别几个概念:
xcdatamodeld文件是可视化的可编辑的模型文件,它是我们可以见到的一个实体文件,是物理的,但并非是数据库,因为它存储的不是数据(实体)本身,而是实体的模型。应用程序先创建或读取模型文件(后缀为xcdatamodeld)生成 NSManagedObjectModel 对象。Document应用程序是一般是通过 NSDocument 或其子类(NSPersistentDocument)从模型文件(后缀为 xcdatamodeld)读取。
(NS)ManagedObjectModel是模型文件中的一个实体,它有三种Property:Attribute(属性),RelationShip(关系), Fetched Property(读取属性)。
(NS)ManagedObject是表示数据文件中的一条记录,每一个 Managed Object 在内存中对应 Entity 的一个数据表示。Managed Object 的成员为 Entity 的 Property 所描述。
(NS)Managed Object Context 的作用相当重要,对数据对象进行的操作都与它有关。当创建一个数据对象并插入 Managed Object Context 中,Managed Object Context 就开始跟踪这个数据对象的一切变动,并在合适的时候提供对 undo/redo 的支持,或调用 Persistent Store Coordinato 将变化保存到数据文件中去。
通常我们将 controller 类(如:NSArrayController,NSTreeController)或其子类与 Managed Object Context 绑定,这样就方便我们动态地生成,获取数据对象等。
(NS)Persistent Store Coordinator:使用 Core Data document 类型的应用程序,通常会从磁盘上的数据文中中读取或存储数据,这写底层的读写就由 Persistent Store Coordinator 来处理。一般我们无需与它直接打交道来读写文件,Managed Object Context 在背后已经为我们调用 Persistent Store Coordinator 做了这部分工作。
NSPersistentDocument 是 NSDocument 的子类。 multi-document Core Data 应用程序使用它来简化对 Core Data 的操作。通常使用 NSPersistentDocument 的默认实现就足够了,它从 Info.plist 中读取 Document types 信息来决定数据的存储格式。这个和前面的有点区别,它下面底层的操作都被打包起来了,直接可以通过managedobjectcontext处理。
(NS)PersistentStore:Core Data可以将数据存储为XML,二进制文件或SQLite文件。在Mac OS X 10.5 Leopard及以后的版本中,开发者也可以通过继承NSPersistentStore类以创建自定义的存储格式。每种方法都有其优缺点,例如XML的可读性,SQLite的节约空间等。一个特定的持久化对象存储是与单个文件或其他外部数据存储相关联的,负责存储中的数据和托管对象上下文中的对象之间的映射。通常情况下,读者与持久化对象存储之间只有一个交互,就是在应用程序中为一个新的外部数据存储指定位置(例如,当用户打开或保存文档)。Core Data的大多数交互则通过托管对象上下文实现。简而言之,(NS)PersistentStore是对物理存储的抽象,但是你不能直接用它,而应该用它的子类NSAtomicStore 和NSIncrementalStore。Core Data提供了四种存储格式SQLite, Binary, XML, and In-Memory (the XML store is not available on iOS),其中Binary和 XML是atomicstore类型的,并因此继承了NSAtomicStore的功能。
现在就比较容易理解下面这幅图了:
1.加注:数据模型是一种部署资源。 在模型中,除了有实体和属性的详细信息外,用Xcode创建的模型还包含了一些额外的视图信息,包括布局、颜色等等。这些信息在运行时不是必须的。模型文件在编译的过程中会删除这些额外信息以保证尽可能高效的加载。xcdatamodel“源”文件会被momc编译器编译为mom的目标文件。
"mom" 位于 /Library/Application Support/Apple/Developer Tools/Plug-ins/XDCoreDataModel.xdplugin/Contents/Resources/,如果你想把它用在自己的 build脚本中,格式是:mom source destination, source 就是Core Data Model文件,destination就是输出的mom文件。