天天看點

iOS學習37資料處理之CoreData

1. CoreData資料庫架構的優勢

 1> CoreData曆史

  CoreData資料持久化架構是Cocoa API 的一部分,首次在iOS5版本的系統中出現,它允許按照實體-屬性-值模型組織資料,并以XML、二級制檔案或者SQLite資料檔案的格式持久化資料。

  CoreData主要提供對象 --- 關系映射(ORM)功能,把OC對象轉化為資料儲存到檔案,也可以資料轉化為OC對象。

 2> CoreData資料庫與Sqlite資料庫的比較

  Sqlite:

  • 基于C接口,需要使用sql語句,但是代碼較為繁瑣
  • 在處理大量資料時,表關系更為直覺
  • 在OC中不是可視化的
  • 可以跨平台使用(iOS和安卓)

  CoreData:

  • 可視化,有undo/redo能力
  • 可以實作多種檔案格式NSSQLiteStoreType、NSBinaryStoreType、NSInMemoryStoreType、NSXMLStoreType
  • 蘋果官方API支援,與iOS結合更緊密
  • 不能跨平台使用,隻支援iOS

 3> CoreData核心對象及關系執行個體(以一個餐館為例)

  

2. CoreData資料庫架構的核心對象

 1> 被管理對象上下文(NSManagedObjectContext)

  NSManagedObjectContext:被管理對象上下文,CoreData中用于操作和使用資料,負責應用和資料庫之間的互動,可以進行增、删、改、查

  資料的儲存需要 NSManagedObjectContext 進行 save 操作

  資料的查詢需要 NSManagedObjectContext 進行 executeFetchRequest 操作(傳回值是數組)

  CoreData提供的是對象關系映射,NSManagedObjectContext操作的都是NSManagedObject對象

 2> 被管理對象相關類

  NSManagedObjectModel:被管理對象模型,管理多個對象

  NSManagedObject:被管理對象,CoreData傳回的資料類型,被管理的對象是根據是根據實體描述生成的

  NSEntityDescription:實體描述類,根據實體建立被管理對象,可以映射出對象

  Entity:實體類,實體是對檔案資料的描述。被管理對象表示實體,實體包含名稱,屬性(字段)和關系,實體的名稱通常和被管理對象名一緻

 3> 持久化存儲和存儲檔案

  資料連接配接器類:NSPersistentStoreCoordinator,持久化存儲調節器,它想要去SQLite資料庫中拿資料必須通過NSPersistentStore。

  NSPersistentStore:持久化存儲,是對實際檔案的一種。Objective-C表示方式,一個被封裝好的底層類,用于存儲資料

  存儲檔案:用來存儲和管理資料的檔案,iOS支援4種存儲類型:NSSQLiteStoreType、NSBinaryStoreType、NSInMemoryStoreType、NSXMLStoreType

 4> 資料查詢

  NSFetchRequest:查詢請求,可以做排序操作,也可以使用謂詞

  NSManagedObjectModel根據NSFetchRequest查詢資料,以數組形式傳回,數組中包含被管理對象(NSManagedObject)

  NSSortDescriptor:排序操作

3. CoreData資料庫的簡單操作 

 1> 建立步驟

  ① 建立帶有CoreData資料庫的工程

  ② 建立實體類與屬性

  ③ 切換可視化關系圖

   

  ④ 建立執行個體類

    

 2> CoreData上下文(NSManagedObjectContext)的建立

  建立NSManagedObjectContext 這個類的對象 一般都用從AppDelegate裡面的拿過來用,不用單獨建立,建立工程勾選 use CoreData 這些代碼AppDelegate就建立好

1 @interface ViewController ()
 2 
 3 #pragma mark - 第二步:聲明屬性【聲明管理對象上下文】(Sqlite中是聲明一個存儲資料路徑的屬性)
 4 @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
 5 
 6 @end
 7 
 8 @implementation ViewController
 9 
10 #pragma mark - 懶加載(與以前有所差別)
11 - (NSManagedObjectContext *)managedObjectContext
12 {
13     // 因為在Appdelegate中已經實作過了,是以這裡是從Appdelegate中去擷取
14     if (!_managedObjectContext) {
15         
16         // 擷取appDelegate對象,使用系統的單例方法建立
17         AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
18         
19         _managedObjectContext = appDelegate.managedObjectContext;
20     }
21     return _managedObjectContext;
22 }
23 
24 @end      

 3> AppDelegate建立好的代碼解析(詳解請看注釋)

  AppDelegate.h

1 #import <UIKit/UIKit.h>
 2 #import <CoreData/CoreData.h>
 3 
 4 @interface AppDelegate : UIResponder <UIApplicationDelegate>
 5 
 6 @property (strong, nonatomic) UIWindow *window;
 7 
 8 /// 管理資料庫上下文的對象,可以進行增、删、改、查
 9 @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
10 
11 /// 管理資料庫中的對象(對象模型),可以将工程中的對象合并(為了更加友善的生成資料庫中的表)
12 @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
13 
14 /// 資料庫持久化協調器,實際的工作都是由它來做的
15 @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
16 
17 - (void)saveContext;
18 - (NSURL *)applicationDocumentsDirectory;
19 
20 @end      

  AppDelegate.m

1 #import "AppDelegate.h"
  2 
  3 @interface AppDelegate ()
  4 
  5 @end
  6 
  7 @implementation AppDelegate
  8 
  9 #pragma mark - Core Data stack
 10 
 11 @synthesize managedObjectContext = _managedObjectContext;
 12 @synthesize managedObjectModel = _managedObjectModel;
 13 @synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
 14 
 15 // 傳回coreData存儲的路徑
 16 - (NSURL *)applicationDocumentsDirectory {
 17     
 18     // 5. 列印coreData存儲的路徑【Sqlite的dbPath字元串路徑】
 19     NSLog(@"filePath = %@", [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]);
 20     
 21     // 傳回coreData存儲的路徑
 22     return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
 23 }
 24 
 25 #pragma mark - 擷取管理對象
 26 - (NSManagedObjectModel *)managedObjectModel {
 27     
 28     if (_managedObjectModel != nil) {
 29         return _managedObjectModel;
 30     }
 31     
 32     // 從應用程式中加載模型檔案
 33     // momd 是編譯後的檔案字尾
 34     NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"UISenior_4_1_CoreData" withExtension:@"momd"];
 35     
 36     _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];// 代表的就是剛剛建立的實體
 37     
 38     return _managedObjectModel;
 39 }
 40 
 41 #pragma mark - 資料持久化協調器(真正幹活的)
 42 // persistentStoreCoordinator的getter方法
 43 - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
 44     
 45     if (_persistentStoreCoordinator != nil) {
 46         return _persistentStoreCoordinator;
 47     }
 48 
 49     // 連接配接器對象關聯的實體模型
 50     _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
 51     
 52     // 定義資料存儲的路徑
 53     NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"UISenior_4_1_CoreData.sqlite"];
 54     
 55     NSError *error = nil;
 56     NSString *failureReason = @"There was an error creating or loading the application's saved data.";
 57     
 58     // NSSQLiteStoreType這個參數決定檔案存儲的形式(sqlite、XML、二進制等)
 59     if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
 60         // 傳回可能出現的錯誤資訊
 61         NSMutableDictionary *dict = [NSMutableDictionary dictionary];
 62         dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
 63         dict[NSLocalizedFailureReasonErrorKey] = failureReason;
 64         dict[NSUnderlyingErrorKey] = error;
 65         error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
 66 
 67         // abort()會導緻應用程式生成一個崩潰日志和終止。雖然它可能是有用的在開發過程中, 但是你不應該使用這個函數在運作的應用程式中。
 68         NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
 69         abort();
 70     }
 71     
 72     return _persistentStoreCoordinator;
 73 }
 74 
 75 #pragma mark - 擷取資料庫上下文
 76 // managedObjectContext的getter方法
 77 - (NSManagedObjectContext *)managedObjectContext {
 78     
 79     if (_managedObjectContext != nil) {
 80         return _managedObjectContext;
 81     }
 82     
 83     // 建立資料持久化協調器
 84     NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
 85     
 86     if (!coordinator) {
 87         return nil;
 88     }
 89     
 90     // 建立managedObjectContext對象
 91     // ConcurrencyType 并發類型,是一個枚舉值
 92     // NSMainQueueConcurrencyType 主隊列并發類型(2)
 93     // NSPrivateQueueConcurrencyType 私有隊列并發類型(1)
 94     // NSConfinementConcurrencyType 限制并發類型(0)
 95     _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
 96     
 97     // 資料連接配接器 關聯 被管理上下文
 98     [_managedObjectContext setPersistentStoreCoordinator:coordinator];
 99     
100     return _managedObjectContext;
101 }
102 
103 #pragma mark - Core Data Saving support
104 
105 - (void)saveContext {
106     NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
107     if (managedObjectContext != nil) {
108         // 同上面的錯誤處理
109         NSError *error = nil;
110         if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
111             
112             NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
113             abort();
114         }
115     }
116 }
117 
118 @end      

 4> 添加對象(詳解請看注釋)

1 - (void)addPerson
 2 {
 3     // 添加的步驟
 4     // 1. 建立Person實體對象,然後開始"context",讓它做好準備,将這個對象添加到資料庫
 5     
 6     /**
 7      *  執行個體對象有兩種[初始化實體對象需要借用NSEntityDescription]
 8      */
 9     
10     // 第一種:
11 //    Person *per = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext];
12     // 第二種:
13     // 先建立一個實體
14     NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.managedObjectContext];
15     // 建立Person對象
16     Person *per = [[Person alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:self.managedObjectContext];
17     
18     // 2. 給對象屬性指派
19     per.personName = @"MBBoy";
20     per.personGender = @"Unkown";
21     per.personAge = @10;
22 
23     // 3. 将對象存入資料庫
24     BOOL result = [_managedObjectContext save:nil];
25     
26     per.personCar = carSet;
27     
28     // 4. 判斷是否插入成功(在AppDelegate中列印位址)
29     if (result) {
30         NSLog(@"添加資料成功");
31     } else {
32         NSLog(@"添加資料失敗");
33     }
34 }      

 5> 删除對象(詳解請看注釋)

1 - (void)deletePerson
 2 {
 3     // 1. 實體化請求類【查詢】
 4     NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
 5     
 6     // 2. 擷取删除的條件(NSPredicate)
 7     request.predicate = [NSPredicate predicateWithFormat:@"personName = 'MBBoy'"];
 8     
 9     // 3. 由context根據删除條件的請求去具體進行删除操作
10     NSArray *resultArray = [self.managedObjectContext executeFetchRequest:request error:nil];
11     
12     // 為了代碼的嚴密需要判斷resultArray中是否有值
13     if (0 == resultArray.count) {
14         NSLog(@"未找到删除對象");
15     } else {
16         // 4. 周遊搜尋出來結果
17         for (Person *per in resultArray) {
18             // 删除查詢到相關的人的資訊
19             [self.managedObjectContext deleteObject:per];
20         }
21         
22         // 5. 進行删除結果的判斷,儲存後删除操作才會寫入檔案
23         BOOL result = [_managedObjectContext save:nil];
24         if (result) {
25             NSLog(@"删除資料成功");
26         } else {
27             NSLog(@"删除資料失敗");
28         }
29     }
30 }      

 6> 更改對象(詳解請看注釋)

1 - (void)updatePerson
 2 {
 3     // 1. 實體化請求類【查詢】
 4     NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
 5     
 6     // 2. 設定查詢的條件
 7     request.predicate = [NSPredicate predicateWithFormat:@"personName = 'MBBoy'"];
 8     
 9     // 3. 由context根據查詢條件的請求去具體進行更新操作
10     NSArray *resultArray = [self.managedObjectContext executeFetchRequest:request error:nil];
11     
12     // 為了代碼的嚴密需要判斷resultArray中是否有值
13     if (0 == resultArray.count) {
14         NSLog(@"未找到更改對象,請檢查謂詞條件!");
15     } else {
16         // 4. 周遊搜尋結果
17         for (Person *per in resultArray) {
18             
19             // 更新查詢到相關的人的資訊
20             per.personName = @"小強";
21             per.personGender = @"卵男";
22             per.personAge = @38;
23         }
24         
25         // 5. 進行删除結果的判斷
26         BOOL result = [_managedObjectContext save:nil];
27         if (result) {
28             NSLog(@"更改資料成功");
29         } else {
30             NSLog(@"更改資料失敗");
31         }
32     }
33 }      

 7> 查詢對象(詳解請看注釋)

1 - (void)selectPerson
 2 {
 3     // 1. 實體化請求類【查詢】
 4     NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
 5     
 6     // 2. 設定查詢的條件
 7     request.predicate = [NSPredicate predicateWithFormat:@"personName like '*B*'"];
 8     
 9     // 3. 根據管理對象上下文執行相關的操作
10     NSArray *resultArray = [self.managedObjectContext executeFetchRequest:request error:nil];
11     
12     if (0 == resultArray.count) {
13         NSLog(@"未找到查詢對象,請檢查謂詞條件!");
14     } else {
15         for (Person *per in resultArray) {
16             NSLog(@"name = %@, gender = %@, age = %@", per.personName, per.personGender, per.personAge);
17         }
18     }
19 }      

  補: 查詢結果的排序

1     // 設定排序方式
2     NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"address" ascending:NO];
3     request.sortDescriptors = @[sort];      

4. CoreData資料庫表關聯操作 

 1> 添加表關系可視化操作

  Car資料庫與Person資料庫建立關聯:

 Person資料庫與Car資料庫建立關聯: 

   

 2> 添加表關系代碼操作

1     // 建立Person對象
 2     Person *per = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.managedObjectContext];
 3     // 2. 給對象屬性指派
 4     per.personName = @"MBBoy";
 5     per.personGender = @"Unkown";
 6     per.personAge = @10;
 7     
 8     ///// 将車的對象通過實體描述類建立出來
 9     Car *audiCar = [NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
10     
11     audiCar.price = @300000;
12     audiCar.color = @"白色";
13     audiCar.brand = @"奧迪A7";
14     
15     Car *benchiCar = [NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
16     
17     benchiCar.price = @500000;
18     benchiCar.color = @"紅色";
19     benchiCar.brand = @"奔馳SUV";
20     
21     // 将兩輛車放到集合中存儲,然後進行指派
22     NSSet *carSet = [NSSet setWithObjects:audiCar, benchiCar, nil];
23     
24     // 3. 将對象存入資料庫
25     BOOL result = [_managedObjectContext save:nil];
26     
27     // 建立一對多關系
28     per.personCar = carSet;
29 
30     // 4. 判斷是否插入成功(在AppDelegate中列印位址)
31     if (result) {
32         NSLog(@"添加資料成功");
33     } else {
34         NSLog(@"添加資料失敗");
35     }      

  補: 一對多表關系時CoreData自動生成基本操作方法

1 // 添加人與一輛車之間的關系
2 - (void)addPersonCarObject:(NSManagedObject *)value;
3 // 删除人與一輛車之間的關系
4 - (void)removePersonCarObject:(NSManagedObject *)value;
5 // 添加人與一組車之間關系
6 - (void)addPersonCar:(NSSet<NSManagedObject *> *)values;
7 // 删除人與一組車之間關系
8 - (void)removePersonCar:(NSSet<NSManagedObject *> *)values;      

  注:此處隻是表間關系删除,并不會删除檔案内資料

5. CoreData資料庫資料的遷移

 1> 概述

   CoreData 支援随着App開發演進而帶來的對象模型(NSManagedObjectModel)更新或修改的管理。模型的改變将導緻不相容(或不能打開)以前版本建立的存儲。如果你要改變你的模型,你就必須要改變現有存儲中的資料 - 即資料存儲格式(store format)——這被稱為資料遷移(migration)

 2> 資料遷移的三個階段

  • 建立基于源執行個體對象的目标執行個體對象;
  • 重建立立聯系;
  • 驗證與儲存;