iOS CoreData(一)

CoreData簡述

CoreData是ios系統推薦給我們的數據存儲方案。在ios中數據存儲有多種方式,包括NSUserDefault、writeToFile...、NSArchiver等,這里我主要說一下CoreData的用法。
??CoreData是數據庫嗎?很顯然不是,它是一個完整的數據處理方案,封裝的是底層的SQL語句,所以不需要我們會寫SQL語句,就能夠對數據進行處理,并提供了NSFetchResultsController可以將處理的結果顯示在UITableView中。但是CoreData也是有缺點的,那就是它不如SQLite性能高。
下面詳述CoreData API及其相關用法。

CoreData詳述

一、CoreData框架

CoreData是ios3.0推出的數據存儲方案。讓我們先看一下CoreData API 主要架構,我們可以看到該API包含了40個類(Classes),3個協議(Protocols),以及3個參考(References)。下面我給大家匯總,一方面讓大家對CoreData整體框架有一個宏觀把握,另一方面也為深入研究做鋪墊。
???????????Tab.1 Classes Lists

Classes Function
1.NSAsynchronousFetchRequest 繼承自:NSPersistentStoreRequest
2.NSBatchDeleteRequest 繼承自:NSPersistentStoreRequest
3.NSBatchUpdateRequest 繼承自:NSPersistentStoreRequest
4.NSSaveChangesRequest 繼承自:NSPersistentStoreRequest
5.NSFetchRequest 繼承自:NSPersistentStoreRequest
6.NSBatchDeleteResult 繼承自:NSPersistentStoreResult
7.NSBatchUpdateResult 繼承自:NSPersistentStoreResult
8.NSAsynchronousFetchResult 繼承自:NSPersistentStoreAsynchronousResult
9.NSPersistentStoreAsynchronousResult 繼承自:NSPersistentStoreResult
10.NSPersistentStoreRequest 繼承自:NSObject
11.NSPersistentStoreResult 繼承自:NSObject
12.NSAtomicStore 繼承自:NSPersistentStore
13.NSIncrementalStore 繼承自:NSPersistentStore
14.NSPersistentStore 繼承自:NSObject
15.NSAtomicStoreCacheNode 繼承自:NSObject
16.NSConstraintConflict 繼承自:NSObject
17.NSEntityDescription 繼承自:NSObject
18.NSEntityMapping 繼承自:NSObject
19.NSEntityMigrationPolicy 繼承自:NSObject
20.NSFetchedResultsController 繼承自:NSObject
21.NSIncrementalStoreNode 繼承自:NSObject
22.NSManagedObject 繼承自:NSObject
23.NSManagedObjectContext 繼承自:NSObject
24.NSManagedObjectID 繼承自:NSObject
25.NSManagedObjectModel 繼承自:NSObject
26.NSMappingModel 繼承自:NSObject
27.NSMergeConflict 繼承自:NSObject
28.NSMergePolicy 繼承自:NSObject
29.NSMigrationManager 繼承自:NSObject
30.NSPersistentContainer 繼承自:NSObject
31.NSPersistentStoreCoordinator 繼承自:NSObject
32.NSPersistentStoreDescription 繼承自:NSObject
33.NSExpressionDescription 繼承自:NSPropertyDescription
34.NSFetchedPropertyDescription 繼承自:NSPropertyDescription
35.NSRelationshipDescription 繼承自:NSPropertyDescription
36.NSAttributeDescription 繼承自:NSPropertyDescription
37.NSPropertyDescription 繼承自:NSObject
38.NSPropertyMapping 繼承自:NSObject
39.NSQueryGenerationToken 繼承自:NSObject
40.NSFetchRequestExpression 繼承自:NSExpression

很多的類吧,有人看暈了嗎?好吧,那我現在列成樹形結構,大家就清晰了。

Fig.1 CoreData類樹形圖

Tab.2 Protocols Lists

Protocols Function
1.NSFetchedResultsControllerDelegate 繼承自:NSObject
2.NSFetchedResultsSectionInfo ---
3.NSFetchRequestResult 繼承自:NSObject

Tab.3 Preference Lists

Protocols Function
1 Core Data Constants
2 Core Data Enumerations
3 Core Data Data Types

上面介紹了CoreData這個API的主要框架,具體可見CoreData官方

二、CoreData實現原理

原理

CoreData存儲數據,有幾個重要的類需要記住,這個我已經在Tab.1中用加粗進行顯示。NSFetchRequest、NSEntityDescription、NSManagedObject、NSManagedObjectContext、NSManagedObjectModel。我們先看一下實現數據存儲方案的原理。

Fig.2 CoreData實現數據存儲原理

幾個重要的類

1. NSManagedObjectModel 數據模型
??可以看做是數據庫的模型結構,包含了各個實體的定義信息。有點像SQLite.sqlite文件,表示一個.xcdatamodeld文件。

//創建方式
// 獲取模型文件路徑
NSURL *modelURL = [[NSBundle mainBundle] URLForResource :modelName withExtension: @"person'];
//根據模型文件創建模型對象
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
//也可以利用下面方法,從應用改程序包中加載.xcdatamodeld模型文件
 NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];

2. NSPersistentStoreCoordinator 持久化存儲協調器
??它是將對象的管理部分和持久化部分捆綁在一起,二者之間需要該調度器進行調節。它是最接近數據底層的。用來設置CoreData存儲類型和存儲路徑。

//創建方式:在創建之前必須創建NSManagedObjectModel模型。
NSPersistentStoreCoordinator *persistent = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSError *error = nil;
NSString *sqlPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,  NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"person.sqlite"];
[persistent addPersistentStoreWithType:NSSQLiteStoreType configuration:nil  URL :[NSURL fileURLWithPath:sqlPath] options:nil  error:&error];
if (error) {
    if (fail) {
        fail(error);
    }
} else 
{
    self.context.persistentStoreCoordinator = self.persistent;
    if (success) {
        success();
    }
}


3. NSManagedObjectContext 管理上下文
??被管理數據的上下文,實際上是對所有數據庫操作的一個緩存層,會把你所有的操作都先緩存起來避免大量磁盤IO造成不流暢,操作完成數據庫后調用save進行持久化。可以理解成用來管理.xcdatamodeld中的數據。

//創建
self.context = [[NSManagedObjectContext alloc] init];
NSError *error = nil;
BOOL result = [self.context save: &error];
    if (!result) {
          if (fail) {
              fail(error);
          }
    } else {
          if (success)
              success();
    }



4. NSEntityDescription
用來描述實體,相當于數據庫表中一組數據描述。

//創建方式:不能用alloc init方式創建,通過傳入上下文和實體名稱,創建一個名稱對應的實體對象(相當于數據庫中一組數據,包含各種字段)。
NSManagedObject  *newEntity = [NSEntityDescription insertNewObjectForEntityForName: entityName inManagedObjectContext:self.context];

三、CoreData實現數據存儲

下面我們在代碼層級上進行說明CoreData的使用。
1. 新建.xcdatamodeld文件。

新建.xcdatamodelId文件

新建完成如下所示。


新建完成后

2. 下面增加實體名稱,可以點擊person.xcdatamodeld下面的Add Entity。

增加實體文件

注意:實體名字可以修改,但是必須以大寫字母開頭。


修改實體名字

修改成功以后如下圖所示。

實體名字修改完成

3. 增加實體里面的鍵值對,也就是增加數據庫里面的字段。

增加字段

4. 創建關聯類操控CoreData實體對象。

創建關聯類

選擇要管理的數據實體模型,這里默認給你已經勾選好了。

選擇數據模型

選擇要管理的實體,這里默認給你已經勾選好了。

選擇管理實體

這里可以修改關聯生成文件的類型是oc還是swift,我選擇oc。

修改關聯文件的類型

選擇person.xcdatamodeld文件,再選擇Editor-->Create NSManageObjectSubclass自動生成四個文件。

關聯生成的四個文件

Command + B 編譯一下。

編譯結果

這個結果很讓我詫異,知道是有的內容重復了但是不知道什么原因,后來在網上還是找到了解決辦法。

解決辦法

就是把紅色框框內部的編譯文件刪除,避免重復編譯。然后就好了。下面我們看看關聯的NSManagedObject的子類里面都是什么。

PersonEntity+CoreDataClass.h文件中
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

NS_ASSUME_NONNULL_BEGIN

@interface PersonEntity : NSManagedObject

@end

NS_ASSUME_NONNULL_END

#import "PersonEntity+CoreDataProperties.h"
PersonEntity+CoreDataClass.m文件中

#import "PersonEntity+CoreDataClass.h"

@implementation PersonEntity

@end

PersonEntity+CoreDataProperties.h文件中

NS_ASSUME_NONNULL_BEGIN

@interface PersonEntity (CoreDataProperties)

+ (NSFetchRequest<PersonEntity *> *)fetchRequest;

@property (nonatomic) double weight;
@property (nullable, nonatomic, copy) NSString *name;
@property (nullable, nonatomic, copy) NSString *location;
@property (nonatomic) int16_t identify;
@property (nonatomic) BOOL gender;
@property (nonatomic) double height;
@property (nonatomic) int16_t age;

@end

NS_ASSUME_NONNULL_END

PersonEntity+CoreDataProperties.m

@implementation PersonEntity (CoreDataProperties)

+ (NSFetchRequest<PersonEntity *> *)fetchRequest {
    return [[NSFetchRequest alloc] initWithEntityName:@"PersonEntity"];
}

@dynamic weight;
@dynamic name;
@dynamic location;
@dynamic identify;
@dynamic gender;
@dynamic height;
@dynamic age;

@end

注意:這里有幾點需要說明:

  • 這四各類都繼承自NSManagedObject。
  • name和location可以是nullable,表示CoreData數據庫存儲的對象可能為nil,字段值可以為NULL。
  • PersonEntity+CoreDataProperties這個類不能利用alloc init方式進行創建。蘋果API提供了幾個專門創建實體對象的方法。

四、CoreData實現數據的增刪改查

1. insert數據的增加

a) 根據Entity和Context獲取一個新的NSManagedObject
    NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.context];
b) 通過setValue:forkey:給NSManagedObject對象賦值
   [newEntity setValue forKey: key];
c) 保存修改
   NSError *error = nil;
   BOOL result = [self.context  save: &error];

2. delete數據的刪除

a) 通過查詢獲得需要刪除的NSManagedObject, 一般用NSPredicate語句。
b) 在for循環中,調用deleteObject:方法逐個刪除。
   [self.context deleteObject: entity];
c) self.context 進行保存。

3. update數據的修改

a) 通過查詢找到需要修改的NSManagedObject 的集合,一般用NSPredicate。
b) 在for循環中,調用NSManagedObject的setValue:forkey: 方法給各個屬性賦值。
c) 上下文進行保存。

4. read數據的查詢

a) 創建請求對象。
  NSFetchRequest *request = [[NSFetchRequest alloc] init];
b) 設置需要查詢的實體描述
  NSEntityDescription *desc = [NSEntityDescription entityForName:self.entityName  inManagedObjectContext: self.context];
c) 設置查詢條件。
   NSPredicate *predicate = [NSPredicate predicateWithFormat:filterString];
d) 將實體和排序都賦值給請求對象。
  request.entity = desc;
  request.predicate = predicate;
  request.sortDescriptors = descArr;
e) 開始查詢
  NSError *error = nil;
  // NSManagedObject對象集合
  NSArray *objs = [self.context executeFetchRequest:request error:&error];
  // 查詢結果數目
  NSUInteger count = [self.context countForFetchRequest:request error:&error];

從上面可以看到,增刪改查四種,查詢是最復雜的,而且刪除和修改也是基于查詢的,可以這么認為查詢如果會了,那么增刪改查就差不多了。其他的就是保存等其他的操作,那就簡單了。

五、CoreData封裝

下面對CoreData進行封裝。

DDCoreDataManager.h文件

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

#define kDDCoreDataManagerModelName        (@"person")

@interface DDCoreDataManager : NSObject

@property (readonly, nonatomic, strong) NSManagedObjectContext *manageObjectContext;
@property (readonly, nonatomic, strong) NSManagedObjectModel *manageObjectModel;
@property (readonly, nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;

+ (instancetype)shareCoreDataManager;

- (void)saveContext;

@end

DDCoreDataManager.m文件

#import "DDCoreDataManager.h"


@implementation DDCoreDataManager

@synthesize manageObjectContext = _manageObjectContext;
@synthesize manageObjectModel = _manageObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

static DDCoreDataManager *coreDataManager;

#pragma mark - 單例

+ (instancetype)shareCoreDataManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        coreDataManager = [[self alloc] init];
    });
    return coreDataManager;
}


#pragma mark - 被管理對象模型

- (NSManagedObjectModel *)manageObjectModel
{
    if (_manageObjectModel != nil) {
        return _manageObjectModel;
    }
    
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:kDDCoreDataManagerModelName withExtension:@"momd"];
    _manageObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _manageObjectModel;
}

#pragma mark - document目錄

- (NSURL *)applicationDocumentDirectory
{
    return [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].lastObject;
}

#pragma mark - 實例化調度器

- (NSPersistentStoreCoordinator *)persistentCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    NSURL *coordinatorStoreURL = [[self applicationDocumentDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.sqlite",kDDCoreDataManagerModelName]];
    
    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self manageObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:coordinatorStoreURL options:nil error:&error]) {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
        dict[NSLocalizedFailureReasonErrorKey] =  @"There was an error creating or loading the application's saved data.";
        dict[NSUnderlyingErrorKey] = error;
        error = [NSError errorWithDomain:@"your error domain" code: 666    userInfo:dict];
        abort();
    }
    
    return _persistentStoreCoordinator;
}


#pragma mark - 實例化上下文context

- (NSManagedObjectContext *)manageObjectContext
{
    if (_manageObjectContext != nil) {
        return _manageObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentCoordinator];
    if (_persistentStoreCoordinator != nil) {
        _manageObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        _manageObjectContext.persistentStoreCoordinator = coordinator;
    }
    return _manageObjectContext;
}

#pragma mark - 上下文進行保存

- (void)saveContext
{
    NSManagedObjectContext *context = [self manageObjectContext];
    if (context != nil) {
        NSError *saveError = nil;
        if ([context hasChanges] && ![context save:&saveError]) {
        // 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 %@, %@", saveError, [saveError userInfo]);
        abort();
        }
    }
    
}

@end

總結

前面對CoreData進行了仔細的說明和封裝,有什么不對的地方還請大家留言和批評指正。

致謝

盡管CoreData出的較早,但是使用起來總感覺沒那么順,看了不少技術大牛的博客,多敲代碼和時間長了才慢慢了解和熟悉。這里借鑒了很多技術大牛的經驗,感謝!

相關資料和博客

  1. ios開發:Core Data 周周周少爺
  2. iOS CoreData介紹和使用(以及一些注意事項)---Pocket (ps:我已經關注了您,哈哈)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容