iOS CoreData介紹和使用(以及一些注意事項(xiàng))

iOS CoreData介紹和使用(以及一些注意事項(xiàng))

最近花了一點(diǎn)時(shí)間整理了一下CoreData,對(duì)于經(jīng)常使用SQLite的我來說,用這個(gè)真的有點(diǎn)用不慣,個(gè)人覺得實(shí)在是沒發(fā)現(xiàn)什么亮點(diǎn),不喜勿噴啊。不過這門技術(shù)的出現(xiàn)也有其存在價(jià)值,這是不可否認(rèn)的事實(shí),即使是不喜歡我們也得去了解一下,因?yàn)槟悴挥脛e人會(huì)用,這年頭都多人開發(fā),多學(xué)點(diǎn)還是有好處的。廢話不多說了,該開始正經(jīng)事了。

CoreData介紹

CoreData是一個(gè)模型層的技術(shù),也是一種持久化技術(shù)(數(shù)據(jù)庫),它能將模型對(duì)象的狀態(tài)持久化到磁盤里,我們不需要使用SQL語句就能對(duì)它進(jìn)行操作。不過在性能方面是弱于直接使用SQLite的。

CoreData使用

理論我就不多說了,覺得我說的不夠詳細(xì)的可以自行搜索關(guān)于更多的CoreData介紹。

  • 創(chuàng)建步驟流程
    • 第一步先創(chuàng)建.xcdatamodeld文件(New File -> iOS -> Core Data ->Data Model)
屏幕快照 2016-07-07 下午10.40.16.png

名字雖然可以任意取,但最好還是取和自己存儲(chǔ)數(shù)據(jù)庫名字一樣的名字。這樣可讀性更高些。(ps:這個(gè)文件就相當(dāng)于數(shù)據(jù)庫文件一樣,數(shù)據(jù)庫文件中可以有多個(gè)表,表中可以有各個(gè)字段值,這里也可以有多個(gè)實(shí)體,每個(gè)實(shí)體有各個(gè)鍵值)

屏幕快照 2016-07-07 下午10.40.40.png

通過上面的操作就可以創(chuàng)建一個(gè).xcdatamodeld文件,現(xiàn)在我們點(diǎn)擊這個(gè)文件,在最下面找到Add Entity按鈕,進(jìn)行實(shí)體添加。

屏幕快照 2016-07-13 下午8.50.10.png

現(xiàn)在我們添加了一個(gè)名字為Entity的實(shí)體,這個(gè)名字是默認(rèn)名字,現(xiàn)在我們可以對(duì)他進(jìn)行名字的更改,對(duì)它雙擊一下即可修改名字,或者在xcode右邊的信息欄中也可以修改

屏幕快照 2016-07-13 下午9.09.38.png

通過上面操作我們已經(jīng)創(chuàng)建好了實(shí)體的名字,現(xiàn)在我們需要往實(shí)體中添加我們需要的鍵值。(ps:相當(dāng)于數(shù)據(jù)庫表中的字段),具體操作看圖說話(實(shí)體之間的關(guān)聯(lián)我就不介紹了,有興趣的可以自行搜索資料,個(gè)人覺得會(huì)了單個(gè)實(shí)體創(chuàng)建外加查詢就行,關(guān)聯(lián)也無非是找到關(guān)聯(lián)的實(shí)體中共同的鍵值從而取得另外一個(gè)實(shí)體對(duì)象,我們可以直接先從一個(gè)實(shí)體取得指定鍵值對(duì)應(yīng)的屬性,再通過屬性值去查詢另一個(gè)實(shí)體,只是沒關(guān)聯(lián)的那么方便而已)。

屏幕快照 2016-07-13 下午9.17.43.png
  • 第二步創(chuàng)建關(guān)聯(lián)類來操控CoreData實(shí)體對(duì)象(選中.xcdatamodeld文件->xcode菜單欄->Edit->Create NSManagedObject Subclass)
屏幕快照 2016-07-13 下午9.35.57.png

選中自己需要關(guān)聯(lián)的.xcdatamodeld文件名稱,點(diǎn)擊下一步即可。

屏幕快照 2016-07-13 下午9.47.24.png

選中.xcdatamodeld文件中需要關(guān)聯(lián)的實(shí)體對(duì)象,點(diǎn)擊下一步然后在選擇存儲(chǔ)目錄即可。

屏幕快照 2016-07-13 下午9.48.06.png

完成后會(huì)發(fā)現(xiàn)自動(dòng)生成了實(shí)體名稱對(duì)應(yīng)的類和擴(kuò)展類(Entity.h/.m和Entity+CoreDataProperties.h/.m)

屏幕快照 2016-07-13 下午10.23.22.png

到這里就完成了整一個(gè)創(chuàng)建的流程,個(gè)人覺得說的有點(diǎn)過于詳細(xì)了(導(dǎo)致看起來有點(diǎn)啰嗦)

  • 注意

我在試驗(yàn)的過程中發(fā)現(xiàn),如果我把關(guān)聯(lián)的類從項(xiàng)目中刪除,在對(duì)實(shí)體名稱再次修改后,重新創(chuàng)建關(guān)聯(lián)類,發(fā)現(xiàn)類名還是原來的實(shí)體名稱顯示,可能xcode沒刷新,反正最后我是刪除實(shí)體重新Add Entity之后才有用,所以為了不必要麻煩,最好還是想好實(shí)體名稱后再創(chuàng)建關(guān)聯(lián)類。

  • 關(guān)聯(lián)類的理解

我就以我自己創(chuàng)建的類來說明

#import "UploadEntity.h"

NS_ASSUME_NONNULL_BEGIN

@interface UploadEntity (CoreDataProperties)

@property (nullable, nonatomic, retain) NSString *fileName;
@property (nullable, nonatomic, retain) NSString *fileSize;
@property (nullable, nonatomic, retain) NSNumber *fileType;
@property (nullable, nonatomic, retain) NSNumber *finishStatus;
@property (nullable, nonatomic, retain) NSData *imageData;
@property (nullable, nonatomic, retain) NSNumber *time;
@property (nullable, nonatomic, retain) NSString *urlPath;

@end

NS_ASSUME_NONNULL_END
  • 該類繼承NSManagedObject類

  • 可以看到在實(shí)體中的每一個(gè)鍵值都被xcode自動(dòng)生成了實(shí)體類的對(duì)象屬性,而且對(duì)于基本數(shù)據(jù)類型被自動(dòng)轉(zhuǎn)成了NSNumber。

  • 每個(gè)對(duì)象的修飾詞都有nullable,表示CoreData數(shù)據(jù)庫存儲(chǔ)的對(duì)象可能為nil(允許存儲(chǔ)nil,就如數(shù)據(jù)庫一樣,字段值也能為NULL),也就是說我們以后從CoreData讀取的數(shù)據(jù)是有可能為nil。

  • 這個(gè)類對(duì)象的創(chuàng)建我們不能用以往的[[NSObject alloc] init]方式去創(chuàng)建,因?yàn)榻?jīng)過驗(yàn)證直接這樣創(chuàng)建它,然后對(duì)屬性賦值會(huì)直接拋出異常,估計(jì)內(nèi)部經(jīng)行了斷言操作吧。蘋果API提供了幾個(gè)專門創(chuàng)建實(shí)體對(duì)象的方法,后面我在講。

  • 因?yàn)楦鞣N限制的原因,比如創(chuàng)建對(duì)象限制等,導(dǎo)致了我不好去封裝,最后不得已想到個(gè)折中辦法(下面會(huì)講)?;具@一輪下來我的興趣缺失了很多。

  • CoreData API介紹

    • NSManagedObjectContext
       這個(gè)對(duì)象有點(diǎn)像SQLite對(duì)象(個(gè)人理解:用來管理.xcdatamodeld中的數(shù)據(jù))。
       負(fù)責(zé)數(shù)據(jù)和應(yīng)用庫之間的交互(CRUD,即增刪改查、保存等接口都是用這個(gè)對(duì)象調(diào)用).
       每個(gè) NSManagedObjectContext 和其他 NSManagedObjectContext 都是完全獨(dú)立的。
       所有的NSManagedObject(個(gè)人理解:實(shí)體數(shù)據(jù))都存在于NSManagedObjectContext中。
       每個(gè)NSManagedObjectContext都知道自己管理著哪些NSManagedObject(個(gè)人理解:實(shí)體數(shù)據(jù))
       // 創(chuàng)建方式(一般這個(gè)對(duì)象最好聲明成成員變量)
       self.context = [[NSManagedObjectContext alloc] init];
       // 保存信息(比較重要的操作,每次更新和插入以及刪除后必須做的操作)
       NSError *error = nil;
       BOOL result = [self.context save:&error];
       if (!result) {
          NSLog(@"添加數(shù)據(jù)失敗:%@",error);
          if (fail) {
              fail(error);
          }
       } else {
          NSLog(@"添加數(shù)據(jù)成功");
          if (success) {
              success();
          }
       }
      
      
    • NSManagedObjectModel
      Core Data的模型文件,有點(diǎn)像SQLite的.sqlite文件(個(gè)人理解:表示一個(gè).xcdatamodeld文件)
      // 創(chuàng)建方式
      // 1.主動(dòng)加載指定名稱的.xcdatamodeld資源
      //獲取模型路徑
      NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelName
                                                  withExtension:@"momd"];
      //根據(jù)模型文件創(chuàng)建模型對(duì)象
      NSManagedObjectModel *model = [[NSManagedObjectModel alloc]
                                          initWithContentsOfURL:modelURL];
       // 2.從應(yīng)用程序包中加載.xcdatamodeld模型文件                              
       NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
      
    • NSPersistentStoreCoordinator
      持久化存儲(chǔ)庫,CoreData的存儲(chǔ)類型(比如SQLite數(shù)據(jù)庫就是其中一種)。
      用來將對(duì)象管理部分和持久化部分捆綁在一起,負(fù)責(zé)相互之間的交流(中介一樣)
      用來設(shè)置CoreData存儲(chǔ)類型和存儲(chǔ)路徑
      對(duì)象和數(shù)據(jù)庫之間的交互不需要我們來關(guān)心,蘋果幫我們做好了。我們只需要面向OC開發(fā)
      // 創(chuàng)建方式
      /**
        注意:創(chuàng)建NSPersistentStoreCoordinator前必須創(chuàng)建NSManagedObjectModel
              個(gè)人理解:好比在一個(gè)數(shù)據(jù)庫中創(chuàng)建表前需要設(shè)置表的所有字段,
                      這里傳入NSManagedObjectModel模型(.xcdatamodeld模型文件),
                      感覺就是讓CoreData知道所有模型中的實(shí)體中的所有鍵值,
                      從而好創(chuàng)建CoreData數(shù)據(jù)庫              
      */ 
      // 以傳入NSManagedObjectModel模型方式初始化持久化存儲(chǔ)庫
      NSPersistentStoreCoordinator *persistent = [[NSPersistentStoreCoordinator alloc]
                                                    initWithManagedObjectModel:model];
      /*
         持久化存儲(chǔ)庫的類型:
         NSSQLiteStoreType  SQLite數(shù)據(jù)庫
         NSBinaryStoreType  二進(jìn)制平面文件
         NSInMemoryStoreType 內(nèi)存庫,無法永久保存數(shù)據(jù)
         雖然這3種類型的性能從速度上來說都差不多,但從數(shù)據(jù)模型中保留下來的信息卻不一樣
         在幾乎所有的情景中,都應(yīng)該采用默認(rèn)設(shè)置,使用SQLite作為持久化存儲(chǔ)庫
       */
      // 添加一個(gè)持久化存儲(chǔ)庫并設(shè)置類型和路徑,NSSQLiteStoreType:SQLite作為存儲(chǔ)庫
      NSError *error = nil;
      // 名字最好和.xcdatamodeld文件的名字一樣
      NSString *sqlPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, 
                                                              NSUserDomainMask, YES) 
                                                                         lastObject] 
                                        stringByAppendingPathComponent:@"xxx.sqlite"];
      [persistent addPersistentStoreWithType:NSSQLiteStoreType configuration:nil 
                                              URL:[NSURL fileURLWithPath:sqlPath]
                                              options:nil 
                                              error:&error];
      if (error) {
          NSLog(@"添加數(shù)據(jù)庫失敗:%@",error);
          if (fail) {
              fail(error);
          }
      } else {
           NSLog(@"添加數(shù)據(jù)庫成功");
          // 設(shè)置上下文所要關(guān)聯(lián)的持久化存儲(chǔ)庫(這一步千萬不要忘記)
          self.context.persistentStoreCoordinator = self.persistent;
          if (success) {
              success();
          }
      }
      
      
    • NSEntityDescription
      用來描述實(shí)體(Entity):相當(dāng)于數(shù)據(jù)庫表中一組數(shù)據(jù)描述(純屬個(gè)人理解)
      通過Core Data從數(shù)據(jù)庫中取出的對(duì)象,默認(rèn)情況下都是NSManagedObject對(duì)象.
       NSManagedObject的工作模式有點(diǎn)類似于NSDictionary對(duì)象,通過鍵-值對(duì)來存取所有的實(shí)體屬性.
       setValue:forkey:存儲(chǔ)屬性值(屬性名為key);
       valueForKey:獲取屬性值(屬性名為key).
       每個(gè)NSManagedObject都知道自己屬于哪個(gè)NSManagedObjectContext
      // 創(chuàng)建方式(用于插入數(shù)據(jù)使用:獲得實(shí)體,改變實(shí)體各個(gè)屬性值,保存后就代表插入)
      /**
        注意:不能用 alloc init方式創(chuàng)建
        通過傳入上下文和實(shí)體名稱,創(chuàng)建一個(gè)名稱對(duì)應(yīng)的實(shí)體對(duì)象
        (相當(dāng)于數(shù)據(jù)庫一組數(shù)據(jù),其中含有多個(gè)字段)
        個(gè)人感覺有種先插入一個(gè)新的Entity從而獲得Entity,在進(jìn)行各屬性賦值
      */
      NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:
                                                                        entityName 
                                                    inManagedObjectContext:self.context];
      
  • CoreData的增刪改查

    上面說了這么多始終沒描述如何去操作它,現(xiàn)在先說說大概步驟。

    • insert
      1.根據(jù)Entity名稱和NSManagedObjectContext獲取一個(gè)新的NSManagedObject
        NSManagedObject *newEntity = [NSEntityDescription
                                      insertNewObjectForEntityForName:entityName 
                                      inManagedObjectContext:self.context];
      2.根據(jù)Entity中的鍵值,一一對(duì)應(yīng)通過setValue:forkey:給NSManagedObject對(duì)象賦值
        [newEntity setValue:value forKey:key];
      3.保存修改
        NSError *error = nil;
        BOOL result = [self.context save:&error];
      
    • delete
      1.通過查詢(設(shè)置好查詢條件)請(qǐng)求取得需要?jiǎng)h除的NSManagedObject的所有集合
      2.通過for循環(huán)調(diào)用deleteObject:方法進(jìn)行逐個(gè)刪除(個(gè)人懷疑這個(gè)for循環(huán)會(huì)不會(huì)導(dǎo)致性能問題),
        暫時(shí)沒發(fā)現(xiàn)其它刪除方法。
        [self.context deleteObject:entity];
      3.保存修改
      
    • update
      1.通過查詢(設(shè)置好查詢條件)請(qǐng)求取得需要修改的NSManagedObject的所有集合
      2.通過for循環(huán)調(diào)用NSManagedObject對(duì)象的setValue:forkey:方法給各個(gè)屬性賦值
      3.保存修改
      
    • read
      1.創(chuàng)建NSFetchRequest查詢請(qǐng)求對(duì)象
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
      2.設(shè)置需要查詢的實(shí)體描述NSEntityDescription
        NSEntityDescription *desc = [NSEntityDescription entityForName:self.entityName
                                                  inManagedObjectContext:self.context];
        request.entity = desc;
      3.設(shè)置排序順序NSSortDescriptor對(duì)象集合(可選)
        request.sortDescriptors = descriptorArray;
      4.設(shè)置條件過濾(可選)
        NSPredicate *predicate = [NSPredicate predicateWithFormat:filterStr];
        request.predicate = predicate;
      5.執(zhí)行查詢請(qǐng)求
        NSError *error = nil;
        // NSManagedObject對(duì)象集合
        NSArray *objs = [self.context executeFetchRequest:request error:&error];
        // 查詢結(jié)果數(shù)目
        NSUInteger count = [self.context countForFetchRequest:request error:&error];
      

      從上面可以看出如果我們僅僅是簡單的對(duì)CoreData進(jìn)行增刪改查的操作的話,稍微費(fèi)點(diǎn)心思的就屬查詢這一塊。里面涉及到了兩個(gè)對(duì)象NSSortDescriptor和NSPredicate,分別用于設(shè)置排序和過濾查詢條件的,尤其是NSPredicate,它不僅僅用于CoreData,還被經(jīng)常用于數(shù)組的過濾。具體對(duì)這兩個(gè)對(duì)象的介紹我就不說了,感覺這一篇寫多了。下面我直接上代碼。不在多講了。

  • CoreData個(gè)人小封裝

    上面提到實(shí)體的創(chuàng)建因素導(dǎo)致了封裝重用的難度,基本上新建一個(gè).xcdatamodeld文件就得做好特意為它寫一些操作接口的準(zhǔn)備。

    • CoreDataAPI.h/.m
      //
      //  CoreDataAPI.h
      //  TedcallStorage
      //
      //  Created by  tedcall on 16/7/1.
      //  Copyright ? 2016年 pocket. All rights reserved.
      //
      
      #import <Foundation/Foundation.h>
      
      @interface CoreDataAPI : NSObject
      /**
       *  獲取數(shù)據(jù)庫存儲(chǔ)的路徑
       */
      @property (nonatomic,copy,readonly) NSString *sqlPath;
      /**
       *  獲取.xcdatamodeld文件的名稱
       */
      @property (nonatomic,copy,readonly) NSString *modelName;
      /**
       *  獲取.xcdatamodeld文件中創(chuàng)建的實(shí)體的名稱
       */
      @property (nonatomic,copy,readonly) NSString *entityName;
      
      /**
       *  創(chuàng)建CoreData數(shù)據(jù)庫
       *
       *  @param entityName 實(shí)體名稱
       *  @param modelName  .xcdatamodeld文件名稱(為nil則主動(dòng)從程序包加載模型文件)
       *  @param sqlPath    數(shù)據(jù)庫存儲(chǔ)的路徑
       *  @param success    成功回調(diào)
       *  @param fail       失敗回調(diào)
       *
       *  @return 返回CoreDataAPI對(duì)象
       */
      - (instancetype)initWithCoreData:(NSString *)entityName modelName:(NSString *)modelName sqlPath:(NSString *)sqlPath success:(void(^)(void))success fail:(void(^)(NSError *error))fail;
      
      /**
       *  插入數(shù)據(jù)
       *
       *  @param dict 字典中的鍵值對(duì)必須要與實(shí)體中的每個(gè)名字一一對(duì)應(yīng)
       *  @param success    成功回調(diào)
       *  @param fail       失敗回調(diào)
       */
      - (void)insertNewEntity:(NSDictionary *)dict success:(void(^)(void))success fail:(void(^)(NSError *error))fail;
      
      /**
       *  查詢數(shù)據(jù)
       *
       *  @param sequenceKeys 數(shù)組高級(jí)排序(數(shù)組里存放實(shí)體中的key,順序按自己需要的先后存放即可),實(shí)體key來排序
       *  @param isAscending  是否上升排序
       *  @param filterStr    過濾語句
       *  @param success      成功后結(jié)果回調(diào)
       *  @param fail         失敗回調(diào)
       */
      - (void)readEntity:(NSArray *)sequenceKeys ascending:(BOOL)isAscending filterStr:(NSString *)filterStr success:(void(^)(NSArray *results))success fail:(void(^)(NSError *error))fail;
      
      /**
       *  刪除數(shù)據(jù)
       *
       *  @param entity  NSManagedObject
       *  @param success 成功回調(diào)
       *  @param fail    失敗回調(diào)
       */
      - (void)deleteEntity:(NSManagedObject *)entity success:(void(^)(void))success fail:(void(^)(NSError *error))fail;
      
      /**
       *  更新數(shù)據(jù)
       *
       *  @param success 成功回調(diào)
       *  @param fail    失敗回調(diào)
       */
      - (void)updateEntity:(void(^)(void))success fail:(void(^)(NSError *error))fail;
      

@end

```
```objc
//
//  CoreDataAPI.m
//  TedcallStorage
//
//  Created by  tedcall on 16/7/1.
//  Copyright ? 2016年 pocket. All rights reserved.
//

/**
 *  知識(shí)點(diǎn):
 NSManagedObject:
     通過Core Data從數(shù)據(jù)庫中取出的對(duì)象,默認(rèn)情況下都是NSManagedObject對(duì)象.
     NSManagedObject的工作模式有點(diǎn)類似于NSDictionary對(duì)象,通過鍵-值對(duì)來存取所有的實(shí)體屬性.
     setValue:forkey:存儲(chǔ)屬性值(屬性名為key);
     valueForKey:獲取屬性值(屬性名為key).
     每個(gè)NSManagedObject都知道自己屬于哪個(gè)NSManagedObjectContext

 NSManagedObjectContext:
    負(fù)責(zé)數(shù)據(jù)和應(yīng)用庫之間的交互(CRUD,即增刪改查、保存等接口都在這個(gè)對(duì)象中).
    所有的NSManagedObject都存在于NSManagedObjectContext中,所以對(duì)象和context是相關(guān)聯(lián)的
    每個(gè) context 和其他 context 都是完全獨(dú)立的
    每個(gè)NSManagedObjectContext都知道自己管理著哪些NSManagedObject

 NSPersistentStoreCoordinator:
    添加持久化存儲(chǔ)庫,CoreData的存儲(chǔ)類型(比如SQLite數(shù)據(jù)庫就是其中一種)
    中間審查者,用來將對(duì)象圖管理部分和持久化部分捆綁在一起,負(fù)責(zé)相互之間的交流(中介一樣)

 NSManagedObjectModel:
    Core Data的模型文件

 NSEntityDescription:
    用來描述實(shí)體:相當(dāng)于數(shù)據(jù)庫表中一組數(shù)據(jù)描述
 */

#import "CoreDataAPI.h"
#import <CoreData/CoreData.h>
@interface CoreDataAPI()
/**
 *  數(shù)據(jù)模型對(duì)象
 */
@property (nonatomic,strong) NSManagedObjectModel *model;
/**
 *  上下文
 */
@property (nonatomic,strong) NSManagedObjectContext *context;
/**
 *  持久性存儲(chǔ)區(qū)
 */
@property (nonatomic,strong) NSPersistentStoreCoordinator *persistent;
@end

@implementation CoreDataAPI

- (instancetype)initWithCoreData:(NSString *)entityName modelName:(NSString *)modelName sqlPath:(NSString *)sqlPath success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
    if (self = [super init]) {
        // 斷言(實(shí)體名稱和存儲(chǔ)路徑是否為nil)
        // ...

        _entityName = entityName;
        _modelName = modelName;
        _sqlPath = sqlPath;
        // 初始化上下文
        self.context = [[NSManagedObjectContext alloc] init];
        if (modelName) {
            //獲取模型路徑
            NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelName withExtension:@"momd"];
            //根據(jù)模型文件創(chuàng)建模型對(duì)象
            self.model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        } else { // 從應(yīng)用程序包中加載模型文件
            self.model = [NSManagedObjectModel mergedModelFromBundles:nil];
        }

        // 以傳入模型方式初始化持久化存儲(chǔ)庫
        self.persistent = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model];
        /*
         持久化存儲(chǔ)庫的類型:
         NSSQLiteStoreType  SQLite數(shù)據(jù)庫
         NSBinaryStoreType  二進(jìn)制平面文件
         NSInMemoryStoreType 內(nèi)存庫,無法永久保存數(shù)據(jù)
         雖然這3種類型的性能從速度上來說都差不多,但從數(shù)據(jù)模型中保留下來的信息卻不一樣
         在幾乎所有的情景中,都應(yīng)該采用默認(rèn)設(shè)置,使用SQLite作為持久化存儲(chǔ)庫
         */
        // 添加一個(gè)持久化存儲(chǔ)庫并設(shè)置類型和路徑,NSSQLiteStoreType:SQLite作為存儲(chǔ)庫
        NSError *error = nil;
        [self.persistent addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:sqlPath] options:nil error:&error];
        if (error) {
            NSLog(@"添加數(shù)據(jù)庫失敗:%@",error);
            if (fail) {
                fail(error);
            }
        } else {
             NSLog(@"添加數(shù)據(jù)庫成功");
            // 設(shè)置上下文所要關(guān)聯(lián)的持久化存儲(chǔ)庫
            self.context.persistentStoreCoordinator = self.persistent;
            if (success) {
                success();
            }
        }
    }

    return self;
}

// 添加數(shù)據(jù)
- (void)insertNewEntity:(NSDictionary *)dict success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
    if (!dict||dict.allKeys.count == 0) return;
    // 通過傳入上下文和實(shí)體名稱,創(chuàng)建一個(gè)名稱對(duì)應(yīng)的實(shí)體對(duì)象(相當(dāng)于數(shù)據(jù)庫一組數(shù)據(jù),其中含有多個(gè)字段)
    NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:self.entityName inManagedObjectContext:self.context];
    // 實(shí)體對(duì)象存儲(chǔ)屬性值(相當(dāng)于數(shù)據(jù)庫中將一個(gè)值存入對(duì)應(yīng)字段)
    for (NSString *key in [dict allKeys]) {
        [newEntity setValue:[dict objectForKey:key] forKey:key];
    }
    // 保存信息,同步數(shù)據(jù)
    NSError *error = nil;
    BOOL result = [self.context save:&error];
    if (!result) {
        NSLog(@"添加數(shù)據(jù)失敗:%@",error);
        if (fail) {
            fail(error);
        }
    } else {
        NSLog(@"添加數(shù)據(jù)成功");
        if (success) {
            success();
        }
    }
}

// 查詢數(shù)據(jù)
- (void)readEntity:(NSArray *)sequenceKeys ascending:(BOOL)isAscending filterStr:(NSString *)filterStr success:(void(^)(NSArray *results))success fail:(void(^)(NSError *error))fail
{
    // 1.初始化一個(gè)查詢請(qǐng)求
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    // 2.設(shè)置要查詢的實(shí)體
    NSEntityDescription *desc = [NSEntityDescription entityForName:self.entityName inManagedObjectContext:self.context];
    request.entity = desc;
    // 3.設(shè)置查詢結(jié)果排序
    if (sequenceKeys&&sequenceKeys.count>0) { // 如果進(jìn)行了設(shè)置排序
        NSMutableArray *array = [NSMutableArray array];
        for (NSString *key in sequenceKeys) {
            /**
             *  設(shè)置查詢結(jié)果排序
             *  sequenceKey:根據(jù)某個(gè)屬性(相當(dāng)于數(shù)據(jù)庫某個(gè)字段)來排序
             *  isAscending:是否升序
             */
            NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:key ascending:isAscending];
            [array addObject:sort];
        }
        if (array.count>0) {
            request.sortDescriptors = array;// 可以添加多個(gè)排序描述器,然后按順序放進(jìn)數(shù)組即可
        }
    }
    // 4.設(shè)置條件過濾
    if (filterStr) { // 如果設(shè)置了過濾語句
        NSPredicate *predicate = [NSPredicate predicateWithFormat:filterStr];
        request.predicate = predicate;
    }
    // 5.執(zhí)行請(qǐng)求
    NSError *error = nil;
    NSArray *objs = [self.context executeFetchRequest:request error:&error]; // 獲得查詢數(shù)據(jù)數(shù)據(jù)集合
    if (error) {
        if (fail) {
            fail(error);
        }
    } else{
        if (success) {
            success(objs);
        }
    }
}

// 更新數(shù)據(jù)
- (void)updateEntity:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
    NSError *error = nil;
    [self.context save:&error];
    if (error) {
        NSLog(@"刪除失?。?@",error);
        if (fail) {
            fail(error);
        }
    } else {
        if (success) {
            success();
        }
    }

}

// 刪除數(shù)據(jù)
- (void)deleteEntity:(NSManagedObject *)entity success:(void(^)(void))success fail:(void(^)(NSError *error))fail
{
    // 傳入需要?jiǎng)h除的實(shí)體對(duì)象
    [self.context deleteObject:entity];
    // 同步到數(shù)據(jù)庫
    NSError *error = nil;
    [self.context save:&error];
    if (error) {
        NSLog(@"刪除失?。?@",error);
        if (fail) {
            fail(error);
        }
    } else {
        if (success) {
            success();
        }
    }
}
@end

```
  • 用法(UploadCoreDataAPI.h/.m)
    //
    //  UploadCoreDataAPI.h
    //  TedcallStorage
    //
    //  Created by  tedcall on 16/7/14.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    @class ResourceModel,DownLoadModel;
    @interface UploadCoreDataAPI : NSObject
    /**
     *  上傳數(shù)據(jù)庫模型名稱
     */
    @property (nonatomic,copy,readonly) NSString *coreDataModelName;
    /**
     *  上傳數(shù)據(jù)庫實(shí)體名稱
     */
    @property (nonatomic,copy,readonly) NSString *coreDataEntityName;
    /**
     *  上傳數(shù)據(jù)庫存儲(chǔ)路徑
     */
    @property (nonatomic,copy,readonly) NSString *coreDataSqlPath;
    + (instancetype)sharedInstance;
    /**
     *  插入上傳記錄
     *
     *  @param model   數(shù)據(jù)模型
     *  @param success 成功回調(diào)
     *  @param fail    失敗回調(diào)
     */
    - (void)insertUploadModel:(ResourceModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail;
    
    /**
     *  更新上傳記錄
     *
     *  @param model   數(shù)據(jù)模型
     *  @param success 成功回調(diào)
     *  @param fail    失敗回調(diào)
     */
    - (void)updateUploadModel:(DownLoadModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail;
    
    /**
     *  刪除一條上傳記錄
     *
     *  @param model   數(shù)據(jù)模型
     *  @param success 成功回調(diào)
     *  @param fail    失敗回調(diào)
     */
    - (void)deleteUploadModel:(DownLoadModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail;
    
    /**
     *  刪除所有上傳記錄
     *
     *  @param success 成功回調(diào)
     *  @param fail    失敗回調(diào)
     */
    - (void)deleteAllUploadModel:(void(^)(void))success fail:(void(^)(NSError *error))fail;
    
    /**
     *  查詢上傳數(shù)據(jù)庫所有數(shù)據(jù)
     *
     *  @param success 成功回調(diào)(finishArray:已完成(DownLoadModel對(duì)象數(shù)組) unfinishedArray:未完成(DownLoadModel對(duì)象數(shù)組))
     *  @param fail    失敗回調(diào)
     */
    - (void)readAllUploadModel:(void(^)(NSArray *finishArray,NSArray *unfinishedArray))success fail:(void(^)(NSError *error))fail;
    @end
    
    
    //
    //  UploadCoreDataAPI.m
    //  TedcallStorage
    //
    //  Created by  tedcall on 16/7/14.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    #import "UploadCoreDataAPI.h"
    #import "CoreDataAPI.h"
    #import "ResourceModel.h"
    #import "DownLoadModel.h"
    static NSString * const modelName = @"Upload";
    static NSString * const entityName = @"UploadEntity";
    static NSString * const sqliteName = @"Upload.sqlite";
    @interface UploadCoreDataAPI()
    @property (nonatomic,strong) CoreDataAPI *uploadData;
    @end
    @implementation UploadCoreDataAPI
    static UploadCoreDataAPI *uploadCoreData = nil;
    + (instancetype)sharedInstance
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            uploadCoreData = [[UploadCoreDataAPI alloc] init];
        });
    
        return uploadCoreData;
    }
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if (uploadCoreData == nil) {
                uploadCoreData = [super allocWithZone:zone];
            }
        });
    
        return uploadCoreData;
    }
    
    - (instancetype)init
    {
        if (self = [super init]) {
            [self initUploadCoreData];
        }
        return self;
    }
    
    - (void)initUploadCoreData
    {
        _coreDataEntityName = entityName;
        _coreDataModelName = modelName;
        _coreDataSqlPath = [[[FileManager shardInstance] getDocumentPath] stringByAppendingPathComponent:sqliteName];
        self.uploadData = [[CoreDataAPI alloc] initWithCoreData:self.coreDataEntityName modelName:self.coreDataModelName sqlPath:self.coreDataSqlPath success:^{
            NSLog(@"initUploadCoreData success");
        } fail:^(NSError *error) {
            NSLog(@"initUploadCoreData fail");
        }];
    
    }
    
    #pragma mark - -- 插入上傳記錄
    - (void)insertUploadModel:(ResourceModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail
    {
        NSString *fileName = model.fileName;
        NSString *fileSize = [NSString stringWithFormat:@"%.2lf",model.size];
        NSString *urlPath = model.path;
        NSNumber *time = [NSNumber numberWithInt:[[DateManager sharedInstance] getSecondsSince1970]];
        NSNumber *fileType = [NSNumber numberWithInt:model.fileType];
        NSNumber *finishStatus = [NSNumber numberWithBool:NO];
        NSDictionary *dict = NSDictionaryOfVariableBindings(fileName,fileSize,urlPath,time,fileType,finishStatus);
    
        [self.uploadData insertNewEntity:dict success:^{
            if (success) {
                success();
            }
        } fail:^(NSError *error) {
            if (fail) {
                fail(error);
            }
        }];
    }
    
    #pragma mark - -- 更新上傳記錄
    - (void)updateUploadModel:(DownLoadModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail
    {
        NSString *filterStr = [NSString stringWithFormat:@"time = %d AND urlPath = '%@' AND fileName = '%@'",model.time,model.urlPath,model.fileName];
        [self.uploadData readEntity:nil ascending:YES filterStr:filterStr success:^(NSArray *results) {
            if (results.count>0) {
                NSManagedObject *obj = [results firstObject];
                [obj setValue:[NSNumber numberWithBool:model.finishStatus] forKey:@"finishStatus"];
                [self.uploadData updateEntity:^{
                    if (success) {
                        success();
                    }
                } fail:^(NSError *error) {
                    if (fail) {
                        fail(error);
                    }
                }];
            }
        } fail:^(NSError *error) {
            if (fail) {
                fail(error);
            }
        }];
    }
    
    #pragma mark - -- 刪除一條上傳記錄
    - (void)deleteUploadModel:(DownLoadModel *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail
    {
        NSString *filterStr = [NSString stringWithFormat:@"time = %d AND urlPath = '%@' AND fileName = '%@'",model.time,model.urlPath,model.fileName];
        [self.uploadData readEntity:nil ascending:YES filterStr:filterStr success:^(NSArray *results) {
            if (results.count>0) {
                NSManagedObject *obj = [results firstObject];
                [self.uploadData deleteEntity:obj success:^{
                    if (success) {
                        success();
                    }
                } fail:^(NSError *error) {
                    if (fail) {
                        fail(error);
                    }
                }];
            }
        } fail:^(NSError *error) {
            if (fail) {
                fail(error);
            }
        }];
    }
    
    #pragma mark - -- 刪除所有上傳記錄
    - (void)deleteAllUploadModel:(void(^)(void))success fail:(void(^)(NSError *error))fail
    {
        [self.uploadData readEntity:nil ascending:YES filterStr:nil success:^(NSArray *results) {
            for (NSManagedObject *obj in results){
                [self.uploadData deleteEntity:obj success:^{
                    if (success) {
                        success();
                    }
                } fail:^(NSError *error) {
                    if (fail) {
                        fail(error);
                    }
                }];
            }
        } fail:^(NSError *error) {
            if (fail) {
                fail(error);
            }
        }];
    }
    
    #pragma mark - -- 查詢所有上傳記錄
    - (void)readAllUploadModel:(void(^)(NSArray *finishArray,NSArray *unfinishedArray))success fail:(void(^)(NSError *error))fail
    {
        [self.uploadData readEntity:nil ascending:YES filterStr:nil success:^(NSArray *results) {
            NSMutableArray *finishArray = [NSMutableArray array];
            NSMutableArray *unfinishedArray = [NSMutableArray array];
            for (NSManagedObject *obj in results) {
                DownLoadModel *model = [[DownLoadModel alloc] init];
                // 獲取數(shù)據(jù)庫中各個(gè)鍵值的值
                model.fileName = [obj valueForKey:@"fileName"];
                model.fileSize = [obj valueForKey:@"fileSize"];
                model.urlPath = [obj valueForKey:@"urlPath"];
                model.time = [[obj valueForKey:@"time"] intValue];
                model.fileType = [[obj valueForKey:@"fileType"] intValue];
                model.imageData = [obj valueForKey:@"imageData"];
                model.finishStatus = [[obj valueForKey:@"finishStatus"] intValue];
                if (model.finishStatus) {
                    [finishArray addObject:model];
                } else {
                    [unfinishedArray addObject:model];
                }
    
            }
            if (success) {
                success(finishArray,unfinishedArray);
            }
        } fail:^(NSError *error) {
            if (fail) {
                fail(error);
            }
        }];
    }
    @end
    

結(jié)語:以上純屬個(gè)人摸索和個(gè)人總結(jié),不對(duì)的地方忘指出。其實(shí)在實(shí)際開發(fā)中不僅僅是增刪改查這么簡單,有時(shí)候會(huì)出現(xiàn)APP已經(jīng)發(fā)布,但是數(shù)據(jù)庫后續(xù)改變了,這就涉及到數(shù)據(jù)庫的遷移,以及一些數(shù)據(jù)安全問題和線程等都是比較深入的,有專研精神的可以自行搜索相關(guān)資料。互勉?。。?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容