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

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

最近花了一點時間整理了一下CoreData回论,對于經(jīng)常使用SQLite的我來說赫粥,用這個真的有點用不慣拒啰,個人覺得實在是沒發(fā)現(xiàn)什么亮點败许,不喜勿噴啊怒允。不過這門技術的出現(xiàn)也有其存在價值,這是不可否認的事實享言,即使是不喜歡我們也得去了解一下峻凫,因為你不用別人會用,這年頭都多人開發(fā)担锤,多學點還是有好處的蔚晨。廢話不多說了乍钻,該開始正經(jīng)事了肛循。

CoreData介紹

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

CoreData使用

理論我就不多說了夹孔,覺得我說的不夠詳細的可以自行搜索關于更多的CoreData介紹。

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

名字雖然可以任意取析孽,但最好還是取和自己存儲數(shù)據(jù)庫名字一樣的名字搭伤。這樣可讀性更高些。(ps:這個文件就相當于數(shù)據(jù)庫文件一樣,數(shù)據(jù)庫文件中可以有多個表袜瞬,表中可以有各個字段值怜俐,這里也可以有多個實體,每個實體有各個鍵值)

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

通過上面的操作就可以創(chuàng)建一個.xcdatamodeld文件邓尤,現(xiàn)在我們點擊這個文件拍鲤,在最下面找到Add Entity按鈕贴谎,進行實體添加。

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

現(xiàn)在我們添加了一個名字為Entity的實體季稳,這個名字是默認名字擅这,現(xiàn)在我們可以對他進行名字的更改,對它雙擊一下即可修改名字景鼠,或者在xcode右邊的信息欄中也可以修改

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

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

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

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

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

選中.xcdatamodeld文件中需要關聯(lián)的實體對象肠骆,點擊下一步然后在選擇存儲目錄即可。

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

完成后會發(fā)現(xiàn)自動生成了實體名稱對應的類和擴展類(Entity.h/.m和Entity+CoreDataProperties.h/.m)

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

到這里就完成了整一個創(chuàng)建的流程塞耕,個人覺得說的有點過于詳細了(導致看起來有點啰嗦)

  • 注意

我在試驗的過程中發(fā)現(xiàn)蚀腿,如果我把關聯(lián)的類從項目中刪除,在對實體名稱再次修改后扫外,重新創(chuàng)建關聯(lián)類莉钙,發(fā)現(xiàn)類名還是原來的實體名稱顯示,可能xcode沒刷新筛谚,反正最后我是刪除實體重新Add Entity之后才有用磁玉,所以為了不必要麻煩,最好還是想好實體名稱后再創(chuàng)建關聯(liá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類

  • 可以看到在實體中的每一個鍵值都被xcode自動生成了實體類的對象屬性蚊伞,而且對于基本數(shù)據(jù)類型被自動轉成了NSNumber。

  • 每個對象的修飾詞都有nullable吮铭,表示CoreData數(shù)據(jù)庫存儲的對象可能為nil(允許存儲nil时迫,就如數(shù)據(jù)庫一樣,字段值也能為NULL)谓晌,也就是說我們以后從CoreData讀取的數(shù)據(jù)是有可能為nil掠拳。

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

  • 因為各種限制的原因逊移,比如創(chuàng)建對象限制等,導致了我不好去封裝龙填,最后不得已想到個折中辦法(下面會講)胳泉。基本這一輪下來我的興趣缺失了很多岩遗。

  • CoreData API介紹

    • NSManagedObjectContext
       這個對象有點像SQLite對象(個人理解:用來管理.xcdatamodeld中的數(shù)據(jù))扇商。
       負責數(shù)據(jù)和應用庫之間的交互(CRUD,即增刪改查宿礁、保存等接口都是用這個對象調(diào)用).
       每個 NSManagedObjectContext 和其他 NSManagedObjectContext 都是完全獨立的案铺。
       所有的NSManagedObject(個人理解:實體數(shù)據(jù))都存在于NSManagedObjectContext中。
       每個NSManagedObjectContext都知道自己管理著哪些NSManagedObject(個人理解:實體數(shù)據(jù))
       // 創(chuàng)建方式(一般這個對象最好聲明成成員變量)
       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的模型文件,有點像SQLite的.sqlite文件(個人理解:表示一個.xcdatamodeld文件)
      // 創(chuàng)建方式
      // 1.主動加載指定名稱的.xcdatamodeld資源
      //獲取模型路徑
      NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelName
                                                  withExtension:@"momd"];
      //根據(jù)模型文件創(chuàng)建模型對象
      NSManagedObjectModel *model = [[NSManagedObjectModel alloc]
                                          initWithContentsOfURL:modelURL];
       // 2.從應用程序包中加載.xcdatamodeld模型文件                              
       NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
      
    • NSPersistentStoreCoordinator
      持久化存儲庫返吻,CoreData的存儲類型(比如SQLite數(shù)據(jù)庫就是其中一種)姑子。
      用來將對象管理部分和持久化部分捆綁在一起,負責相互之間的交流(中介一樣)
      用來設置CoreData存儲類型和存儲路徑
      對象和數(shù)據(jù)庫之間的交互不需要我們來關心测僵,蘋果幫我們做好了街佑。我們只需要面向OC開發(fā)
      // 創(chuàng)建方式
      /**
        注意:創(chuàng)建NSPersistentStoreCoordinator前必須創(chuàng)建NSManagedObjectModel
              個人理解:好比在一個數(shù)據(jù)庫中創(chuàng)建表前需要設置表的所有字段,
                      這里傳入NSManagedObjectModel模型(.xcdatamodeld模型文件)捍靠,
                      感覺就是讓CoreData知道所有模型中的實體中的所有鍵值沐旨,
                      從而好創(chuàng)建CoreData數(shù)據(jù)庫              
      */ 
      // 以傳入NSManagedObjectModel模型方式初始化持久化存儲庫
      NSPersistentStoreCoordinator *persistent = [[NSPersistentStoreCoordinator alloc]
                                                    initWithManagedObjectModel:model];
      /*
         持久化存儲庫的類型:
         NSSQLiteStoreType  SQLite數(shù)據(jù)庫
         NSBinaryStoreType  二進制平面文件
         NSInMemoryStoreType 內(nèi)存庫,無法永久保存數(shù)據(jù)
         雖然這3種類型的性能從速度上來說都差不多剂公,但從數(shù)據(jù)模型中保留下來的信息卻不一樣
         在幾乎所有的情景中希俩,都應該采用默認設置吊宋,使用SQLite作為持久化存儲庫
       */
      // 添加一個持久化存儲庫并設置類型和路徑纲辽,NSSQLiteStoreType:SQLite作為存儲庫
      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ù)庫成功");
          // 設置上下文所要關聯(lián)的持久化存儲庫(這一步千萬不要忘記)
          self.context.persistentStoreCoordinator = self.persistent;
          if (success) {
              success();
          }
      }
      
      
    • NSEntityDescription
      用來描述實體(Entity):相當于數(shù)據(jù)庫表中一組數(shù)據(jù)描述(純屬個人理解)
      通過Core Data從數(shù)據(jù)庫中取出的對象,默認情況下都是NSManagedObject對象.
       NSManagedObject的工作模式有點類似于NSDictionary對象,通過鍵-值對來存取所有的實體屬性.
       setValue:forkey:存儲屬性值(屬性名為key);
       valueForKey:獲取屬性值(屬性名為key).
       每個NSManagedObject都知道自己屬于哪個NSManagedObjectContext
      // 創(chuàng)建方式(用于插入數(shù)據(jù)使用:獲得實體,改變實體各個屬性值璃搜,保存后就代表插入)
      /**
        注意:不能用 alloc init方式創(chuàng)建
        通過傳入上下文和實體名稱拖吼,創(chuàng)建一個名稱對應的實體對象
        (相當于數(shù)據(jù)庫一組數(shù)據(jù),其中含有多個字段)
        個人感覺有種先插入一個新的Entity從而獲得Entity这吻,在進行各屬性賦值
      */
      NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:
                                                                        entityName 
                                                    inManagedObjectContext:self.context];
      
  • CoreData的增刪改查

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

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

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

  • CoreData個人小封裝

    上面提到實體的創(chuàng)建因素導致了封裝重用的難度学少,基本上新建一個.xcdatamodeld文件就得做好特意為它寫一些操作接口的準備剪个。

    • 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ù)庫存儲的路徑
       */
      @property (nonatomic,copy,readonly) NSString *sqlPath;
      /**
       *  獲取.xcdatamodeld文件的名稱
       */
      @property (nonatomic,copy,readonly) NSString *modelName;
      /**
       *  獲取.xcdatamodeld文件中創(chuàng)建的實體的名稱
       */
      @property (nonatomic,copy,readonly) NSString *entityName;
      
      /**
       *  創(chuàng)建CoreData數(shù)據(jù)庫
       *
       *  @param entityName 實體名稱
       *  @param modelName  .xcdatamodeld文件名稱(為nil則主動從程序包加載模型文件)
       *  @param sqlPath    數(shù)據(jù)庫存儲的路徑
       *  @param success    成功回調(diào)
       *  @param fail       失敗回調(diào)
       *
       *  @return 返回CoreDataAPI對象
       */
      - (instancetype)initWithCoreData:(NSString *)entityName modelName:(NSString *)modelName sqlPath:(NSString *)sqlPath success:(void(^)(void))success fail:(void(^)(NSError *error))fail;
      
      /**
       *  插入數(shù)據(jù)
       *
       *  @param dict 字典中的鍵值對必須要與實體中的每個名字一一對應
       *  @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ù)組高級排序(數(shù)組里存放實體中的key,順序按自己需要的先后存放即可)版确,實體key來排序
       *  @param isAscending  是否上升排序
       *  @param filterStr    過濾語句
       *  @param success      成功后結果回調(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.
//

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

 NSManagedObjectContext:
    負責數(shù)據(jù)和應用庫之間的交互(CRUD禁偎,即增刪改查、保存等接口都在這個對象中).
    所有的NSManagedObject都存在于NSManagedObjectContext中阀坏,所以對象和context是相關聯(lián)的
    每個 context 和其他 context 都是完全獨立的
    每個NSManagedObjectContext都知道自己管理著哪些NSManagedObject

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

 NSManagedObjectModel:
    Core Data的模型文件

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

#import "CoreDataAPI.h"
#import <CoreData/CoreData.h>
@interface CoreDataAPI()
/**
 *  數(shù)據(jù)模型對象
 */
@property (nonatomic,strong) NSManagedObjectModel *model;
/**
 *  上下文
 */
@property (nonatomic,strong) NSManagedObjectContext *context;
/**
 *  持久性存儲區(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]) {
        // 斷言(實體名稱和存儲路徑是否為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)建模型對象
            self.model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        } else { // 從應用程序包中加載模型文件
            self.model = [NSManagedObjectModel mergedModelFromBundles:nil];
        }

        // 以傳入模型方式初始化持久化存儲庫
        self.persistent = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model];
        /*
         持久化存儲庫的類型:
         NSSQLiteStoreType  SQLite數(shù)據(jù)庫
         NSBinaryStoreType  二進制平面文件
         NSInMemoryStoreType 內(nèi)存庫盒至,無法永久保存數(shù)據(jù)
         雖然這3種類型的性能從速度上來說都差不多,但從數(shù)據(jù)模型中保留下來的信息卻不一樣
         在幾乎所有的情景中士修,都應該采用默認設置枷遂,使用SQLite作為持久化存儲庫
         */
        // 添加一個持久化存儲庫并設置類型和路徑,NSSQLiteStoreType:SQLite作為存儲庫
        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ù)庫成功");
            // 設置上下文所要關聯(lián)的持久化存儲庫
            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;
    // 通過傳入上下文和實體名稱棋嘲,創(chuàng)建一個名稱對應的實體對象(相當于數(shù)據(jù)庫一組數(shù)據(jù)酒唉,其中含有多個字段)
    NSManagedObject *newEntity = [NSEntityDescription insertNewObjectForEntityForName:self.entityName inManagedObjectContext:self.context];
    // 實體對象存儲屬性值(相當于數(shù)據(jù)庫中將一個值存入對應字段)
    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.初始化一個查詢請求
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    // 2.設置要查詢的實體
    NSEntityDescription *desc = [NSEntityDescription entityForName:self.entityName inManagedObjectContext:self.context];
    request.entity = desc;
    // 3.設置查詢結果排序
    if (sequenceKeys&&sequenceKeys.count>0) { // 如果進行了設置排序
        NSMutableArray *array = [NSMutableArray array];
        for (NSString *key in sequenceKeys) {
            /**
             *  設置查詢結果排序
             *  sequenceKey:根據(jù)某個屬性(相當于數(shù)據(jù)庫某個字段)來排序
             *  isAscending:是否升序
             */
            NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:key ascending:isAscending];
            [array addObject:sort];
        }
        if (array.count>0) {
            request.sortDescriptors = array;// 可以添加多個排序描述器痪伦,然后按順序放進數(shù)組即可
        }
    }
    // 4.設置條件過濾
    if (filterStr) { // 如果設置了過濾語句
        NSPredicate *predicate = [NSPredicate predicateWithFormat:filterStr];
        request.predicate = predicate;
    }
    // 5.執(zhí)行請求
    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
{
    // 傳入需要刪除的實體對象
    [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ù)庫實體名稱
     */
    @property (nonatomic,copy,readonly) NSString *coreDataEntityName;
    /**
     *  上傳數(shù)據(jù)庫存儲路徑
     */
    @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對象數(shù)組) unfinishedArray:未完成(DownLoadModel對象數(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ù)庫中各個鍵值的值
                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
    

結語:以上純屬個人摸索和個人總結网沾,不對的地方忘指出。其實在實際開發(fā)中不僅僅是增刪改查這么簡單蕊爵,有時候會出現(xiàn)APP已經(jīng)發(fā)布辉哥,但是數(shù)據(jù)庫后續(xù)改變了,這就涉及到數(shù)據(jù)庫的遷移,以及一些數(shù)據(jù)安全問題和線程等都是比較深入的醋旦,有專研精神的可以自行搜索相關資料恒水。互勉K瞧搿?芤ぁ!

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末箩张,一起剝皮案震驚了整個濱河市甩骏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌先慷,老刑警劉巖饮笛,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異论熙,居然都是意外死亡福青,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門脓诡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來无午,“玉大人,你說我怎么就攤上這事祝谚∠艹伲” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵交惯,是天一觀的道長次泽。 經(jīng)常有香客問我,道長席爽,這世上最難降的妖魔是什么意荤? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮只锻,結果婚禮上玖像,老公的妹妹穿的比我還像新娘。我一直安慰自己齐饮,他們只是感情好捐寥,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沈矿,像睡著了一般上真。 火紅的嫁衣襯著肌膚如雪咬腋。 梳的紋絲不亂的頭發(fā)上羹膳,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音根竿,去河邊找鬼陵像。 笑死就珠,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的醒颖。 我是一名探鬼主播妻怎,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泞歉!你這毒婦竟也來了逼侦?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤腰耙,失蹤者是張志新(化名)和其女友劉穎榛丢,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挺庞,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡晰赞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了选侨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掖鱼。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖援制,靈堂內(nèi)的尸體忽然破棺而出戏挡,到底是詐尸還是另有隱情,我是刑警寧澤晨仑,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布增拥,位于F島的核電站,受9級特大地震影響寻歧,放射性物質(zhì)發(fā)生泄漏掌栅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一码泛、第九天 我趴在偏房一處隱蔽的房頂上張望猾封。 院中可真熱鬧,春花似錦噪珊、人聲如沸晌缘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽磷箕。三九已至,卻和暖如春阵难,著一層夾襖步出監(jiān)牢的瞬間岳枷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留空繁,地道東北人殿衰。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像盛泡,于是被迫代替她去往敵國和親闷祥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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