Core Data

使用Core Data我们可以很方便实现数据存储而不用关心存储细节。


在Xcode的工程模板中,有3个模板(Master-Detail Application、Utility Application和Empty Application模板)可以直接为工程添加Core Data支持。其他的模板则需要我们自己手动添加Core Data支持。


我们先看一下Xcode自动生成的Core Data支持代码,主要在AppDelegate中。

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;

@end

NSManagedObjectContext,它是被管理对象上下文(MOC)类,在上下文中可以查找、删除和插入对象,然后通过栈同步到持久化对象存储。

NSManagedObjectModel,它是被管理对象模型(MOM)类,是系统中的“实体”,与数据库中的表等对象对应。

NSPersistentStoreCoordinator,它是持久化存储协调器(PSC)类,在持久化存储之上提供了一个接口,可以把它看成为数据库的连接。


除了MOC、MOM和PSC外,还有“持久化对象存储”,它们一起构成Core Data堆栈。


持久化对象存储(Persistent Object Store,POS)执行所有底层的从对象到数据的转换,并负责打开和关闭数据文件。它有3种持久化实现方式:SQLite、二进制文件和内存形式。


Core Data堆栈如下图所示,


有一个或多个被管理对象上下文,它连接到一个持久化存储协调器。一个持久化存储协调器连接到一个或多个持久化对象存储。持久化对象存储与底层存储文件关联。一个持久化存储协调器可以管理多个被管理对象模型。一个持久化存储协调器就意味着一个Core Data堆栈。通过Core Data堆栈可以实现数据查询、插入、删除以及修改等操作。


再回来看AppDelegate.h,除了三个Core Data相关的属性,还有两个方法。其中applicationDocumentsDirectory返回应用的沙箱目录,其实现是:

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

这个方法与之前提到的获取沙箱路径的方法比较像,只不过这里返回值是NSURL,之前的方法返回的是路径的字符串表示:

NSArray *documents= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath=[documents objectAtIndex:0];


另外一个方法是我们关注的重点:

- (void)saveContext
{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
             // Replace this implementation with code to handle the error appropriately.
             // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        } 
    }
}

该方法用于保存被管理对象上下文,当我们修改数据之后需要通过该方法保存修改。


数据保存的实现中用到了NSManagedObjectContext,顺着这条线,我们看看managedObjectContext的getter方法:

// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

第一次获取调用该方法时,需要alloc一个NSManagedObjectContext对象。创建完成后,需要给NSManagedObjectContext对象设置PSC,因此这里需要访问persistentStoreCoordinator的getter方法:

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreData.sqlite"];
    
    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        /*
         Replace this implementation with code to handle the error appropriately.
         
         abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
         
         Typical reasons for an error here include:
         * The persistent store is not accessible;
         * The schema for the persistent store is incompatible with current managed object model.
         Check the error message to determine what the actual problem was.
         
         
         If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
         
         If you encounter schema incompatibility errors during development, you can reduce their frequency by:
         * Simply deleting the existing store:
         [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
         
         * Performing automatic lightweight migration by passing the following dictionary as the options parameter:
         @{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES}
         
         Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
         
         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    
    
    return _persistentStoreCoordinator;
}

同样,第一次访问时需要alloc一个NSPersistentStoreCoordinator,并且需要用到NSManagedObjectModel对象来初始化。初始化完成后,还需要为该对象添加持久化数据存储。其中指定了存储类型,存储类型可以是下面三个常量:

1、NSSQLiteStoreType,数据持久化类型是SQLite;

2、NSBinaryStoreType,数据持久化类是二进制文件;

3、NSInMemoryStoreType,数据持久化类型是内存。


在初始化PSC时,我们需要NSManagedObjectModel对象,下面看看managedObjectModel的getter方法:

// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreData" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}


上面关于Core Data的代码都是Xcode模板自动生成的,从代码调用关系很容易得到头文件中定义的三个Core Data相关的属性的getter方法调用顺序:managedObjectContext ---> persistentStoreCoordinator --->  managedObjectModel。


看完了Xcode模板自动生成的Core Data相关代码后,我们自己手动添加Core Data支持代码。

1、建模和生成实体

这里我们会用到模型文件(模型文件一般是在数据库设计阶段用可视化建模工具创建的数据库模型描述文件),Core Data可以利用它可视化设计数据库,生成实体类的Objective-C代码和SQLite数据库文件。


(1)建模

如果采用模板生成,会创建一个与工程名相同的数据模型文件(<工程名>.xcdatamodeld),如果不采用模板,则创建过程是选择File->New->File->iOS->Core Data->Data Model。这里我们创建的数据模型文件是CoreDataDemo.xcdatamodeld。这里有一点需要注意,在代码中我们使用该模型文件时指定的后缀名是momd并不是xcdatamodeld。因为CoreDataDemo.xcdatamodeld文件在编译发布时,变成了CoreDataDemo.momd。


打开数据模型文件,其界面如下图所示:



在这里我们可以创建实体、实体属性和实体关系等。


创建实体:


在实体的数据模型检查器中设置实体类:



然后通过File->New->File->iOS->Core Data->NSManagedObject subclass生成实体类。


#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>


@interface NoteManagedObject : NSManagedObject

@property (nonatomic, retain) NSDate * date;
@property (nonatomic, retain) NSString * content;

@end

NoteManagedObject实体类需要被Core Data管理,因此需要继承NSManagedObject类。而之前我们创建的类大部分是继承NSObject类,它们没有被Core Data管理。


#import "NoteManagedObject.h"


@implementation NoteManagedObject

@dynamic date;
@dynamic content;

@end


@dynamic是告诉编译器,代码中用@dynamic修饰的属性,其getter和setter方法会在程序运行的时候或者用其他方式动态绑定。NSManagedObject类对象的属性一般是从Core Data的属性中生成的,Core Data框架会在程序运行的时候为此类属性生成getter和setter方法。至此,我们的实体对象就创建好了。


接下来Core Data中的对象NSManagedObjectContext、NSPersistentStoreCoordinator、NSManagedObjectModel相关的代码参考上面Xcode的自动生成。


下面用Core Data实现CRUD方法。

#pragma mark - CRUD supported by Core Data

-(NSArray*)findAll
{
    
    //NSEntityDescription是实体关联的描述类,通过指定的名字获得实例对象,实体的名字是在数据模型文件中定义的。
    NSEntityDescription* entityDescription = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:self.managedObjectContext];
    
    //NSSortDescriptor是排序描述类,用于指定排序字段以及排序方式
    NSSortDescriptor* sortDescriptor =[[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
    
    //NSFetchRequest是数据提取请求类,用于查询
    NSFetchRequest* request =[[NSFetchRequest alloc] init];
    
    //把实体描述设置到请求对象中
    [request setEntity:entityDescription];
    
    //把排序描述设置到请求对象中
    [request setSortDescriptors:@[sortDescriptor]];
    
    NSError* error = nil;
    return [self.managedObjectContext executeFetchRequest:request error:&error];
}

-(NSArray*)findById:(NoteManagedObject*)model
{
    NSEntityDescription* entityDescription = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:self.managedObjectContext];
    NSSortDescriptor* sortDescriptor =[[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
    NSFetchRequest* request =[[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
    [request setSortDescriptors:@[sortDescriptor]];
    
    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"date = %@",model.date];
    [request setPredicate:predicate];
    
    NSError* error = nil;
    return [self.managedObjectContext executeFetchRequest:request error:&error];
}

-(BOOL)add:(NoteManagedObject*)model
{
    NSError* error = nil;
    return [self.managedObjectContext save:&error];
}

-(void)remove:(NoteManagedObject*)model
{
    NSEntityDescription* entityDescription = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:self.managedObjectContext];
    NSFetchRequest* request =[[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"date = %@",model.date];
    [request setPredicate:predicate];
    NSError* requestError = nil;
    NSArray* list = [self.managedObjectContext executeFetchRequest:request error:&requestError];
    if([list count] > 0){
        NoteManagedObject* note = [list lastObject];
        [self.managedObjectContext deleteObject:note];
        NSError* saveError = nil;
        [self.managedObjectContext save:&saveError];
    }
}

-(void)modify:(NoteManagedObject*)model
{
    NSEntityDescription* entityDescription = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:self.managedObjectContext];
    NSFetchRequest* request =[[NSFetchRequest alloc] init];
    [request setEntity:entityDescription];
    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"date = %@",model.date];
    [request setPredicate:predicate];
    NSError* requestError = nil;
    NSArray* list = [self.managedObjectContext executeFetchRequest:request error:&requestError];
    if([list count] > 0){
        NoteManagedObject* note = [list lastObject];
        note.content = model.content;
        NSError* saveError = nil;
        [self.managedObjectContext save:&saveError];
    }
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值