CoreData的介紹和使用

一.CoreData是什么?

  • CoreData是iOS SDK里的一個很強大的框架,允許程序員以面向對象的方式存儲和管理數據。使用CoreData框架,程序員可以輕松有效地通過面向對象的接口管理數據
  • CoreData是一個模型層的技術。幫助建立代表程序狀態的模型層,CoreData也是一種持久化技術,能將模型對象的狀態持久化到磁盤,但他最重要的特點是:CoreData不僅是一個加載、保存數據的框架,它還能和內存中的數據很好的共事。
  • 在數據操作過程中,無需編寫任何SQL語句。
  • CoreData使用包括實體實體間的關系,以及查找符合某些條件實體的請求等內容。
  • 開發者可以在純對象層上查找與管理這些數據,而不必擔心存儲和查找的實現細節。
  • CoreData框架最早出現在Mac OS 10.4 Tiger與iOS 3.0系統,經過成千上萬的應用程序以及數以百萬用戶的反復的我驗證,CoreData確實已經是一套非常成熟的框架。
  • CoreData利用了Objective-C語言和運行時,巧妙地集成了CoreFoundation框架。是一個易于使用的框架,不僅可以優雅第管理對象圖,而且在內存管理方面表現異常優異。

二.怎么學習CoreData

  • 誤區:CoreData不是一個數據庫,不要用數據庫的眼光去看待CoreData
  • CoreData不是應用程序的數據庫,也不是將數據持久化保存到數據庫的API。CoreData是一個用于管理對象圖的框架。CoreData可以把對象圖寫入磁盤從而持久化保存。
  • CoreData Stack(技術堆棧):如果能夠理解CoreData Stack中的各個成員所扮演的角色,那么使用CoreData 就不會在感覺到困難了。

三.Core Data Stack

CoreData stack是CoreData的核心,由一組CoreData核心對象組成

  • NSManagedObjectContext 對象管理上下文:負責管理模型的對象的集合
  • NSManagedObjectModel 被管理的對象模型:負責管理對象模型
  • NSPersistentStoreCordinator 存儲調度器:負責將數據保存到磁盤的

三者之間關系示意圖:

Snip20160608_3.png

分為兩部分:

  • 對象圖管理
  • 數據持久化
    在這兩部分的中間,即堆棧中間,是持久化存儲調度器(Persistent Coordinator,PSC)。通過它將對象圖管理部分和持久化部分綁在一起。當這兩部分中的一部分需要和另一部分交互,將通過PSC來調節。
Snip20160608_4.png

蘋果建議,常見的使用解決方案:

Snip20160608_5.png

四.CoreData的使用

1.要使用Core Data,首先新建一個單例類專門用來管理CoreData的相關操作,需要導入CoreData框架

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface HMCoreDataManager : NSObject

///管理對象上下文
@property (nonatomic, strong)  NSManagedObjectContext *managedObjectContext;
///管理對象模型
@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
///持久化存儲調度器
@property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;

+ (instancetype)sharedInstance;
- (void)saveContext;
@end

表結構:NSEntityDescription
表記錄:NSManagedObject
數據庫存放方式:NSPersistentStoreCoordinator(持久化存儲協調者)
數據庫操作:NSManagedObjectContext(被管理的對象上下文)

2.懶加載三個核心CoreData對象
NSManagedObjectContext( 對象管理上下文)

- (NSManagedObjectContext *)managedObjectContext{

    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    
    //實例化
    //ConcurrencyType:并發(性)
    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    
    //指定上下文所屬的存儲調度器
    _managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;

    return _managedObjectContext;
}

NSManagedObjectModel(被管理的對象模型)

- (NSManagedObjectModel *)managedObjectModel{


    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    
    //獲取模型描述文件URL, .xcdatamodeld文件編譯后在bundle里生成.momd文件
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    
    //實例化 -  指定模型描述文件
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
    
    return _managedObjectModel;
    
}

NSPersistentStoreCordinator(存儲調度器)

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{

    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    //實例化 - 指定管理對象模型
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
    
    //添加存儲器
    //Type:存儲類型, 數據庫/XML/二進制/內存
    //configuration:不需要額外配置,可以為nil
    //URL:數據保存的文件的URL 這里我們放到documents里
    //options:可以為空
    
    NSURL *fileURL = [[self applicationDocumentsURL] URLByAppendingPathComponent:@"myData.db"];
    
    [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:fileURL options:nil error:nil];
    
    
    return _persistentStoreCoordinator;
}

3.接著要使用Code Data,首先需要定義模型文件,描述應用程序中的所有實體(Entities)

Snip20160608_6.png

4.創建項目中需要用到的實體(Entities),例如,創建一個Person實體(類),以及添加一些姓名,年齡,城市等屬性。

Snip20160608_7.png

5.生成實體的模型文件

Snip20160515_14.png
Snip20160515_15.png
Snip20160515_16.png

點擊Next后就會生成相關的模型文件

Snip20160608_8.png

五.CoreData常見操作

1.新增記錄
使用NSEntityDescription來創建對象,賦值后使用相應的context保存即可

    //創建對象
    Person *p = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:[HMCoreDataManager sharedInstance].managedObjectContext];   
    //賦值
    p.name = @"李四";
    p.age = @(30);
    
    //保存
 [[HMCoreDataManager sharedInstance].managedObjectContext save:nil];

2.刪除記錄
使用context的deleteObject:刪除被管理的模型對象后保存即可

    //取出聯系人數據
    ContactsObject *aContact = [self.fetchedResultsController objectAtIndexPath:indexPath];

    //使用所屬上下文刪除數據 - (記得保存同步到數據庫)
    [[HMCoreDataManager sharedInstance].managedObjectContext deleteObject:aContact];
    
    //保存
    [aContact save];

3.修改記錄
直接修改模型對象后通過context保存即可

4.查詢記錄
對使用CoreData進行存儲的數據進行一定條件的查詢后取出來使用
(1)謂詞--NSPredicate
作用:根據一定的條件從數據庫中篩選出符合的數據

案例:從數組中篩選出用戶年齡在5-15之間 同時用戶姓名中包含“1”字符串

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name
CONTAINS '1' && %K BETWEEN {%d, %d}", @"age", 5, 15];

NSArray *result = [ PersonArr filteredArrayUsingPredicate:predicate];

l謂詞的條件指令

1.比較運算符 > 、< 、== 、>= 、<= 、!=
例:@"number >= 99"

2.范圍運算符:IN 、BETWEEN
例:@"number BETWEEN {1,5}"
      @"address IN {'shanghai','nanjing'}"

3.字符串本身:SELF 
例:@"SELF == 'APPLE'"

 4.字符串相關:BEGINSWITH、ENDSWITH、CONTAINS
例:  @"name CONTAIN[cd] 'ang'"  //包含某個字符串
      @"name BEGINSWITH[c] 'sh'"    //以某個字符串開頭
      @"name ENDSWITH[d] 'ang'"    //以某個字符串結束

5.通配符:LIKE
例:@"name LIKE[cd] '*er*'"   //*代表通配符,Like也接受[cd].
      @"name LIKE[cd] '???er*'"

*注*: 星號 "*" : 代表0個或多個字符
      問號 "?" : 代表一個字符 

6.正則表達式:MATCHES
例:NSString *regex = @"^A.+e$"; //以A開頭,e結尾
      @"name MATCHES %@",regex

注:[c]*不區分大小寫 , [d]不區分發音符號即沒有重音符號, [cd]既不區分大小寫,也不區分發音符號。

7. 合計操作
ANY,SOME:指定下列表達式中的任意元素。比如,ANY children.age < 18。
ALL:指定下列表達式中的所有元素。比如,ALL children.age < 18。
NONE:指定下列表達式中沒有的元素。比如,NONE children.age < 18。它在邏輯上等于NOT (ANY ...)。
IN:等于SQL的IN操作,左邊的表達必須出現在右邊指定的集合中。比如,name IN { 'Ben', 'Melissa', 'Nick' }。

提示:
1. 謂詞中的匹配指令關鍵字通常使用大寫字母
2. 謂詞中可以使用格式字符串
3. 如果通過對象的key
path指定匹配條件,需要使用%K

(2)使用查詢結果控制器(NSFetchedResultsController)

NSFetchedResultsControlle --- 用來管理查詢結果的控制器(功能類控制器,并不是視圖喲~)

主要功能:
1.根據一定條件查詢出相應的數據
步驟:
1) 實例化查詢請求對象

NSFetchRequest*fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Person"];
  1. 實例化排序對象
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"name"ascending:YES];
  1. 設置請求的排序
fetchRequest.sortDescriptors = @[sort];

4)實例化查詢結果控制器(指定請求對象, 上下文, section*在屬性的哪個Key)

_fetchedResultsController= [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext: Context sectionNameKeyPath:@"section"
cacheName:nil];

5)執行查詢

NSError *error = nil;

[_fetchedResultsController
performFetch:&error];

NSLog(@"%@",error);

6)查詢完的結果會在這里(一個模型對象數組--實例化請求時指定的實體名類型的對象)

_fetchedResultsController.fetchedObjects
綜上所述查詢結果控制器的懶加載代碼如下:
- (NSFetchedResultsController *)fetchedResultsController{

    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    
    //查詢請求 - 指定要查詢的實體名
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:[ContactsEntity entityName]];
    
    //排序描述器  指定根據某個key的升序或降序
    NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:@"namePinYin" ascending:YES];

    //排序描述器數組: 可以有多個排序描述,例如:名字一樣的情況下,在根據電話號碼某種順序,如果沒有二級的排序標準系統會有默認排序
    NSSortDescriptor *sortDescPhone = [NSSortDescriptor sortDescriptorWithKey:@"phoneNum" ascending:YES];
    request.sortDescriptors = @[sortDesc,sortDescPhone];
    
    //實例化
    //Request: 查詢請求
    //managedObjectContext: 要查詢的上下文
    //sectionNameKeyPath: 分組依據的字段
    //cacheName:緩存名
    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[CZCoreDataManager sharedInstance].managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:nil];
    
    //設置代理
    _fetchedResultsController.delegate = self;
    
    //執行查詢
    NSError *error = nil;
    [_fetchedResultsController performFetch:&error];
    
    if (error) {      
        NSLog(@"%@",error);
    }
    
    //打印結果 .fetchedObjects保存著沒有分組的所有查詢結果
    for (ContactsEntity *aContacts in _fetchedResultsController.fetchedObjects) {
        
        NSLog(@"%@",aContacts.name);
    }
    return _fetchedResultsController;
}

2.為tableView而生
NSFetchedResultsController查詢結果和tableView數據源分組結構類似,可以非常方便的給tableView顯示數據, 以及修改數據

Snip20160608_9.png

1.section數組
self.fetchedResultsController.sections

2.根據索引取出對象
[self.fetchedResultsController objectAtIndexPath:indexPath]

3.section的indexTitle數組
self.fetchedResultsController.sectionIndexTitles

一堆代理方法:

控制器里的模型對象發生改變時調用,會告訴你之前和之后的索引,改變類型(插入/移動/刪除/..)
controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:

對象模型section改變時調用
controller:didChangeSection:atIndex:forChangeType:

找我要分組名sectionName
controller:sectionIndexTitleForSectionName:

控制器里的模型對象已經發生改變時調用
controllerDidChangeContent:

控制器里的模型對象即將發生改變時調用
controllerWillChangeContent:

直接使用上下文查詢( NSManagedObjectContext)

步驟:

1)實例化查詢請求
NSFetchRequest*fetchRequest = [[NSFetchRequestalloc] init];

2)實例化實體描述(指定要去哪個實體里查&使用哪個上下文)
NSEntityDescription*entity = [NSEntityDescriptionentityForName:[Contact entityName] inManagedObjectContext:[CoreDataTool shareInstance].managedObjectContext];

3)將查詢請求設置實體名
[fetchRequest setEntity:entity];

4)實例化 查詢條件(謂詞)
NSPredicate*predicate = [NSPredicatepredicateWithFormat:@"self.name contains 'a'"];

  1. 設置查詢請求的 查詢條件
    [fetchRequest setPredicate:predicate];

6)實例化一個排序器(指定某個屬性的升降順序)
NSSortDescriptor*sortDescriptor=[[NSSortDescriptoralloc] initWithKey:@"namePinYin"ascending:YES];

7)設置查詢請求的排序器(可以多個)
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];

8)使用管理上下文執行查詢語句
NSError*error = nil; NSArray*fetchedObjects=[[CoreDataToolshareInstance].managedObjectContext executeFetchRequest:fetchRequest error:&error];

六.最后

對象復用的概念:

對象的復用大大提高了數據使用的性能 ,效率比直接用SQL操作數據庫高得多.
當你要訪問一個對象,可能這個對象的情況,存在下面三種可能:

(1)對象已經在context中,這種操作基本上是沒有任何代價的。
(2)對象不在context中,但是因為你最近從store中取出過對象,所以持久化存儲協調器緩存了對象的值,這個操作還算效率還可以。
(3) 對象既不在context中,持久化存儲協調器也沒有緩存,效率比較低,代價高.

操作耗費最昂貴的情況是(3),當context和持久化存儲協調器都是第一次訪問這個對象,這中情況必須通過store從SQLite數據庫取回。第三種情況比(1)和(2)需要付出更多代價。

要顯著提升性能時
[NSManagedObjectContext objectRegisteredForID:]

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

推薦閱讀更多精彩內容