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,緣由很簡單就是喜歡比較透明的編碼方式。

相關文章
相關標籤/搜索