Coredata第三課 資料查詢,Coredata資料查詢
問題
小明班上最近月考了,老師大明想要給一部分優秀的同學進行獎勵,而另外一部分要進行查漏補缺。大明決定將總分排名前10的,各科成績排名前10的以及排名最後10名的按從高到低的順序找出來。以前大明都是在家用筆一個個划出來。不過最近大明在長沙戴維營教育接受了殘酷的iOS培訓,決定裝逼一把,給自己的“腎6+”開發了一款應用。只要各科老師將成績提交給他,就可以直接看到這些學生的成績了,並且各種曲線、柱狀圖、餅圖。每個學生的情況就好比沒穿衣服一樣”透明“。現在的問題是,大明並不想自己去實現各種篩選和排序演算法。
解決方案
很快大明就想到了戴維營教育的部落格上Core Data除了簡單的存取功能外,還具備各種取資料的方法。
一、資料擷取
Core Data中擷取資料必須通過NSFetchRequest
進行。我們有兩種方式擷取NSFetchRequest
對象。
- 通過實體名稱建立
NSFetchRequest
對象。
這種方式其實就是我們在前面兩篇文章中用來擷取資料的技巧。
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];//或者NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];fetchRequest.entity = entity;
//使用managedModel擷取fetchRequest模版NSFetchRequest *fetchRequest = [appDelegate.managedObjectModel fetchRequestTemplateForName:@"personFR"];
- 我們可以指定fetchRequest的結果類型來擷取不同資料,如儲存的對象、結果數目等。
// NSFetchRequest *fetchRequest = [appDelegate.managedObjectModel fetchRequestTemplateForName:@"personFR"]; //如果需要改變結果的類型,不能使用從模版產生的request對象 NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"]; //擷取結果總數 fetchRequest.resultType = NSCountResultType;
不過我們也不只一種擷取結果數目的方式。在Context裡面提供了一系列的操作request的方法,其中就包括了擷取結果數目的功能。
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];//擷取結果數目NSUInteger count = [context countForFetchRequest:fetchRequest error:nil];
二、篩選結果集
大明已經可以得到所有學生的成績資訊了,接下來要做的就是對它們進行排序和篩選。
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];//排序描述符,按score降序排列NSSortDescriptor *sort01 = [NSSortDescriptor sortDescriptorWithKey:@"score" ascending:NO];//可以同時按多個屬性進行排序fetchRequest.sortDescriptors = @[sort01];NSArray *result = [context executeFetchRequest:fetchRequest error:nil];if (result) { _people = [NSMutableArray arrayWithArray:result]; for (NSObject *obj in _people) { NSLog(@"%@", [obj valueForKey:@"score"]); }}
結果:
2015-02-04 10:54:16.599 02-02-CoreData01[5832:276345] 992015-02-04 10:54:16.600 02-02-CoreData01[5832:276345] 602015-02-04 10:54:16.600 02-02-CoreData01[5832:276345] 562015-02-04 10:54:16.600 02-02-CoreData01[5832:276345] 45
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];NSSortDescriptor *sort01 = [NSSortDescriptor sortDescriptorWithKey:@"score" ascending:NO];fetchRequest.sortDescriptors = @[sort01];//限制只取前十,其實這是有問題的,萬一有重複的分數,後面的就取不到了。fetchRequest.fetchLimit = 10;NSArray *result = [context executeFetchRequest:fetchRequest error:nil];
- 使用NSPredicate篩選成績高於90分的學生
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"score >= 90"];fetchRequest.predicate = predicate;
進階
上面的這些資料擷取方式都是同步的方式,如果資料量比較大的話,會顯著的影響到程式的效能和使用者體驗。Core Data中也提供了非同步資料擷取功能。
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;NSManagedObjectContext *context = appDelegate.managedObjectContext;NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];NSSortDescriptor *sort01 = [NSSortDescriptor sortDescriptorWithKey:@"score" ascending:NO];fetchRequest.sortDescriptors = @[sort01];fetchRequest.fetchLimit = 2;//非同步請求NSAsynchronousFetchRequest *asyncRequst = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult *result) { for (NSObject *obj in result.finalResult) { NSLog(@"%@", [obj valueForKey:@"score"]); }}];//執行非同步請求[context executeRequest:asyncRequst error:nil];
注意: 在使用非同步請求的時候,需要設定NSManagedContext對象的並發類型,否則會出錯。
2015-02-04 12:12:50.709 02-02-CoreData01[6083:300576] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSConfinementConcurrencyType context <NSManagedObjectContext: 0x7fb27b72c5f0> cannot support asynchronous fetch request <NSAsynchronousFetchRequest: 0x7fb27b71d750> with fetch request <NSFetchRequest: 0x7fb27b7247a0> (entity: Person; predicate: ((null)); sortDescriptors: (( "(score, descending, compare:)")); limit: 2; type: NSManagedObjectResultType; ).'
解決辦法是在建立Context對象的時候,設定它的並發類型。
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];if (!coordinator) { return nil;}//建立Context對象,並設定並發類型_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];[_managedObjectContext setPersistentStoreCoordinator:coordinator];
參考資料
本文檔由長沙戴維營教育整理。