iOS-CoreData技术

前言sql

CoreData不是数据库,而是对象模型,它提供了对象-关系映射(ORM)功能,能将OC对象保存到SQLite数据库文件中,也能够将数据库数据还原成OC对象;不须要掌握SQL语法也能够操做数据库,有点相似.Net的EF框架。数据库

 

1、CoreData须要使用几个对象进行操做。NSManagedObjectModel对象,加载模型文件,读取app中的全部实体信息;NSPersistentStoreCoordinator对象,添加持久化库(这里采起SQLite数据库);NSManagedObjectContext对象,拿到这个上下文对象操做实体,进行CRUD操做。若是你是在建立项目的时候就勾选了CoreData,那么会在AppDelegate文件中自动生成相关代码,这里采起自定义封装类的方法,独立封装成单例类。安全

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

@interface Database : NSObject

+ (Database *)database;
- (void)saveContext;

@property (nonatomic, strong, readonly) NSManagedObjectContext *objectContext;
@property (nonatomic, strong, readonly) NSManagedObjectModel *objectModel;
@property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *store;

@end

#import "Database.h"

@implementation Database

@synthesize objectContext = _objectContext;
@synthesize objectModel = _objectModel;
@synthesize store = _store;

- (void)saveContext {
    
    NSError *saveError = nil;
    NSManagedObjectContext *context = self.objectContext;
    if ([context hasChanges] && ![context save:&saveError]) {
        
        if (saveError) {
            
            NSLog(@"save error -- %@", [saveError userInfo]);
            abort();
        }
    }
}

+ (Database *)database {
    
    static Database *database;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        database = [[Database alloc] init];
    });
    return database;
}

// 建立模型对象
- (NSManagedObjectModel *)objectModel {
    
    if (!_objectModel) {
        
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"ZHModel" withExtension:@"momd"];
        _objectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    }
    return _objectModel;
}

// 建立管理模型的上下文对象
- (NSManagedObjectContext *)objectContext {
    
    if (!_objectContext) {
        
        _objectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_objectContext setPersistentStoreCoordinator:self.store];
    }
    return _objectContext;
}

// 建立持久化存储协调器
- (NSPersistentStoreCoordinator *)store {
    
    if (!_store) {
        
        // 设置sqlite本地文件存放地址;
        NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ZHModel.sqlite"];
        NSError *error = nil;
        NSLog(@"path -- %@", storeURL);
        
        // 须要数据迁移时,所设置的选项;
        NSDictionary *optionDic = @{
                                    NSMigratePersistentStoresAutomaticallyOption: @(YES),
                                    NSInferMappingModelAutomaticallyOption: @(NO)
                                    };
        
        _store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.objectModel];
        // 此处将NSPersistentStoreCoordinator对象做为对象模型与本地sqlite文件中间的协调器;
        [_store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:optionDic error:&error];
        
        if (error) {
            
            NSLog(@"store error -- %@", [error userInfo]);
            abort();
        }
    }
    return _store;
}

// sqlite文件存放目录;
- (NSURL *)applicationDocumentsDirectory {
    
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

@end

 

2、CoreData并非线程安全的,对于ManagedObject以及ManagedObjectContext的访问都只能在对应的线程上进行,而不能夸线程。并行方案也有不少种,性能各有不一样,一种是Notification方式(简单来讲,就是不一样的线程使用不一样的context进行操做,当一个线程的context发生变化后,利用notification来通知另外一个线程Context,另外一个线程调用mergeChangesFromContextDidSaveNotification来合并变化);另外一种是child/parent context方式(ChildContext和ParentContext是相互独立的。只有当ChildContext中调用Save了之后,才会把这段时间来Context的变化提交到ParentContext中,ChildContext并不会直接提交到NSPersistentStoreCoordinator中, parentContext就至关于它的NSPersistentStoreCoordinator),当ParentContext执行save后,才会把Context的变化保存到本地文件。今天我所讨论的是Notification方式,给出几个关键的地方,Notification方式是共用NSPersistentStoreCoordinator的;app

self.mainContext = [Database database].objectContext;// 运行在主队列的context
self.privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];// 运行在私有队列的context
[self.privateContext setPersistentStoreCoordinator:_mainContext.persistentStoreCoordinator]; // 二者共用着一个persistentStoreCoordinator

 

一、并行的解决方案之Notification第一步,子线程中的context添加数据并保存,执行insertData方法。框架

// 在私有队列中添加保存数据,添加成功后会发出NSManagedObjectContextDidSaveNotification通知;
- (void)insertData {
    
    TestEntity2 *testEntity = [NSEntityDescription insertNewObjectForEntityForName:@"TestEntity2" inManagedObjectContext:self.privateContext];
    testEntity.title = [NSString stringWithFormat:@"entity%u", arc4random() % 100];
    
    [self.privateContext performBlock:^{
        
        NSError *saveError = nil;
        if ([self.privateContext hasChanges]) {
            
            [self.privateContext save:&saveError];
        }
    }];
}

 

二、privateContext保存成功后,发出NSManagedObjectContextDidSaveNotification通知,mainContext经过mergeChangesFromContextDidSaveNotification合并context的变化。dom

// 接收到数据变化保存的通知,主队列执行合并操做。
mergeObject = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {
        
        if ([note.object isEqual:self.privateContext]) {
            
            dispatch_async(dispatch_get_main_queue(), ^{
                
                [self.mainContext performBlock:^{
                    
                    [self.mainContext mergeChangesFromContextDidSaveNotification:note];
                    [self fetchData];
                }];
            });
        }
    }];

 

总结async

持久化存储框架CoreData,FMDB,二者在项目中应用普遍,但不一样之处也很明显。性能

一、CoreData不是线程安全的,而FMDB是线程安全的(使用FMDatabaseQueue);fetch

二、个人理解CoreData数据迁移的不一样,好比数据库表的结构删除字段或者是修改字段名,因为SQLite不支持删除数据表的列以及修改字段名,因此FMDB须要编写SQL语句逻辑(从新建立新表、而后填充旧数据、删除旧表以及修改新表名称),CoreData则在新的对象模型中作新旧表的字段对应和删除对象模型的属性。编码

三、本人喜欢使用FMDB,缘由很简单就是喜欢比较透明的编码方式。

相关文章
相关标签/搜索