CoreData Learning note
后續(xù)閱讀:
https://www.objc.io/issue-4/core-data-overview.html
中文:http://www.cocoachina.com/ios/20130911/6981.html
01: 存?。?/h3>
首先開頭用最簡單的小例子。
//MARK: - Save Data
func saveName(name: String){
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedContext)
let person = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)
person.setValue(name, forKey: "name")
do {
try managedContext.save()
people.append(person)
} catch let error as NSError{
print("Could not save \(error),\(error.userInfo)")
}
}
//MARK: - Fetch Data
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Person")
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
people = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
02.
可以使用BinaryData來存儲圖像,但是巨大的圖像直接存儲在數(shù)據(jù)庫中會導(dǎo)致性能降低,選擇類型為BD的Attribute,然后可以在右邊的屬性選擇框中勾選Allows External Storage,類似所以存儲在外部,提高數(shù)據(jù)訪問性能。
Transformable類型用于存儲那些實現(xiàn)了NSCoding protocol 的對象,比如 UIColor,UIColor 實現(xiàn)了NSSecureCoding。
03. 不要過于依賴 NSManagedObject
雖然使用KVC很便捷,但是盡量不要過于依賴KVC。
04.自動創(chuàng)建Entity的子類:
打開。xcfatamodeld文件 -> Editor -> Create NSManagedObject Subclass
創(chuàng)建出來的每個 Entity 對應(yīng)了兩個文件,普通的文件僅僅包含了所有的 action 操作,Entity 的 所有屬性都用 extension 的方式進行了實現(xiàn)。所有的屬性在Extension中聲明的時候前面都加上了@NSManaged,這個標(biāo)示通知了編譯器,這個 property 會在runtime的時候提供,而不是在編譯的時候。
一般的模式下,property 是由內(nèi)存中的實例變量實現(xiàn)的,但是在managedObject中,是由managed object context實現(xiàn)的,compile time 并不知道。
創(chuàng)建了屬于自定義的Entity的Managed Object的子類有兩個好處:
· 隔離的KVC,編譯器可以獲取 Property,而不是通過 KVC 的方式,方便編寫程序。
· 方便重寫方法。當(dāng)時注意Apple文檔中標(biāo)出了一些從來都不應(yīng)該重寫的方法。
05. 結(jié)合子類 NSManagedObject 去實現(xiàn)添加和 Retrieve 的 Demo:
這里也就是增加的Demo:
let bowtie = NSEntityDescription.insertNewObjectForEntityForName("Bowtie", inManagedObjectContext: managedObjectContext) as! Bowtie
bowtie.name = "My bow tie"
bowtie.lastWorn = NSDate()
do {
try managedObjectContext.save()
} catch let error as NSError {
print("Save error\(error.localizedDescription)")
}
//Retrieve test bow tie
do {
let request = NSFetchRequest(entityName: "Bowtie")
let ties = try managedObjectContext.executeFetchRequest(request) as! [Bowtie]
let sample : Bowtie = ties[0]
print("Name = \(sample.name), Worn: \(sample.lastWorn)")
}catch let error as NSError {
print("Fetching error: \(error.localizedDescription)")
}
06. Data Validation:
選擇Entity的Property,然后右邊可以設(shè)置該項屬性的最大值、最小值和默認(rèn)值。
07. 具體的操作對象:
具體這一部分可以添加常用的方法:會在后續(xù)添加。
http://justsee.iteye.com/blog/1881110
NSManagedObjectContext
表示被操作數(shù)據(jù)的上下文環(huán)境。類似于一種持續(xù)性的數(shù)據(jù)庫連接,可以做增刪查等操作。
而且只有在調(diào)用Save方法的時候,這些所有的改動才會被提交,否則是不會在持久層做任何改動的。
支持撤銷和重做。
NSManagedObjectModel
表示被管理的數(shù)據(jù)模型,這里包含著所有對象的表格信息,所有數(shù)據(jù)結(jié)構(gòu),包括他們之間的關(guān)系。
在這里添加實體的屬性,添加實體和實體之間的關(guān)系。
所以NSManagedObjectModel其實包含著NSEntityDescription和NSPropertyDescription,前者相當(dāng)于數(shù)據(jù)庫中的一個表,后者相當(dāng)于表中的一列。NSPropertyDescription可以描述實體的基本屬性(Attributes)、實體之間的關(guān)系(Relationships)還有 查詢屬性(FetchedProperty)。
查詢屬性對應(yīng)NSFetchedPropertyDescription對象。
Persistent Store
持久化存儲層,是由文件或者外部數(shù)據(jù)庫組成的,大多數(shù)情況下訪問持久化層全部由上下文Context來完成。
CoreData 提供了四種持久化層的方案:
非原子訪問的:NSQLiteStoreType
原子訪問的:BSXMLStoreType、NSBinaryStoreType、NSInMemoryStoreType。
XML是將數(shù)據(jù)存為XML文件,只在 OS X 平臺下可用。Binary的方法是存為一個Data文件。InMemory的方式是不會對數(shù)據(jù)進行真正意義上的持久化,全部存儲在內(nèi)存中,當(dāng)應(yīng)用程序退出時,數(shù)據(jù)也就消失了。
NSPersistentStoreCoordinator
持久化存儲助理的存在相當(dāng)于和數(shù)據(jù)持久層的連接器,SQLite的話就是和數(shù)據(jù)庫的連接,它設(shè)置著數(shù)據(jù)庫的路徑名字、位置、存儲方式和存儲的時機。
CoreData其實就相當(dāng)于一個棧,棧的底層是持久化存儲層,棧頂是Context,所以一般簡單的需求我們只需要使用棧頂?shù)?code>Context,那么NSPersistentStoreCoordinator就是棧中層的一層存在,協(xié)調(diào)上棧的上下層關(guān)系。
負(fù)責(zé)理解NSManagedObjectModel和去NSPersistentStore中執(zhí)行相應(yīng)操作。
補充NSManagedObjectContext
NSManagedObjectContext就像內(nèi)存中的便簽紙,臨時修改你的ManagedObject,所以知道你提交save(),否則持久化層是不會有變化的。
上下文管理這
Managed Object的生命周期,管理的同時提供了很多高級特征可以給以使用,比如排序,Validation、關(guān)系管理等。一個
Managed Object是不能獨立于Context存在的,即有Managed Object就一定會有它的Context,也可以通過 Managed Object 的.managedObjectContext屬性取得它的Context對象。一旦設(shè)置了
Managed Object的Context,那么在Object的很長的生命周期中就會一直跟這個特定的Context關(guān)聯(lián)。一個應(yīng)用程序可以有很多個
Context,你可以創(chuàng)建兩個Context指向一些相同的PersistentStore持久化層。Context并不是線程安全的,所以對于Managed Object也是一樣的,它們只可以在創(chuàng)建他們的那個線程上進行操作。
08. 創(chuàng)建自己的線程棧
創(chuàng)建:CoreDataStack.swift
第一部分創(chuàng)建 Model 的名字和 Document 的路徑URL:
創(chuàng)建新的 .swift 文件
import CoreData
class CoreDataStack {
let modelName = "Dog Walk"
//Document's URL
private lazy var applciationDocumentDirectory: NSURL = {
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()
}
第二步給類添加懶加載的三個屬性 NSManagedObjectContext、NSPersistentStoreCoordinator和NSManagedObjectModel:
//NSManagedObjectContext
//Note: ConcurrencyType 的具體參數(shù)會在后面補充添加,暫時先使用 .MainQueueConcurrencyType
//創(chuàng)建出來Context是完全沒有意義的,直到設(shè)置了Context的PersistentStoreCoordinator
lazy var context: NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = self.psc
return managedObjectContext
}()
//NSPersistentStoreCoordinator
//對StoreCoordinator做懶加載,StoreCoordinator是介與PersistentStore(s)和ObejctModel之間的,所以至少需要一個PersistentStore。
private lazy var psc: NSPersistentStoreCoordinator = {
//coordinator init,傳入Model,Model指所有的Entity和所有的relationship
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
// PersisitentStore 的物理存儲路徑
let url = self.applciationDocumentDirectory.URLByAppendingPathComponent(self.modelName)
do{
// 一些Option配置:
let options = [NSMigratePersistentStoresAutomaticallyOption : true]
//addPersistentStoreWithType 選擇一個SQLite的type,使用SQLite作為存儲模式。
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL:url, options: options)
} catch {
print("Error adding persistnet store.")
}
return coordinator
}()
//NSManagedObjectModel
//這里包含著MainBundle里面的momb文件里面的 `.xcdatamodeld` 文件,就是Xcode圖形化設(shè)計Entity和Relationship的那個文件,使用它來創(chuàng)建ManagedObjectModel
private lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = NSBundle.mainBundle().URLForResource(self.modelName, withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
添加的每 Property 都對應(yīng)著 Core Data 的重要組件,使用Lazy Loading,每個組件都依賴其他的組件。
這些 Property 中只有NSManagedObjectContext是 Public 的 Property,其余的都是 private 修飾。private 修飾的組件外界不需要獲取的。
另外,NSPersistentStoreCoordinator可以通過 NSManagedObjectContext的 Public property 獲取。
而所有的NSManagedObjectModel和NSPersistentStore都可以通過NSPersistentStoreCoordinator的Public property 來獲取。
對StoreCoordinator做懶加載,它是介與PersistentStore(s)和ObejctModel之間的,所以至少需要一個PersistentStore。
最后添加一個方法:
//保存的方法
func saveContent () {
if context.hasChanges {
do {
try context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
}
}
在AppDelegate中添加LazyLoading的iVar:
lazy var coreDataStack = CoreDataStack()
在didFinishLaunchingWIthOption的方法中可以給rootViewController中的Context賦值了:
let navigationController = window!.rootViewController as! UINavigationController
let viewController = navigationController.topViewController as! ViewController
viewController.managedContext = coreDataStack.context
接下來在合適的時機調(diào)用SaveContent方法,這里就是在applicationDidEnterBackground和applicationWillTerminate代理方法中調(diào)用:
coreDataStack.saveContent()
到這里,一個CoreData的棧類就創(chuàng)建完成了,并且已經(jīng)實現(xiàn)了在應(yīng)用退出的時候?qū)?Context 進行 saveing 的操作
09.高級查詢
簡單的查詢通過創(chuàng)建 NSFetchRequest 來從 CoreData 中取得數(shù)據(jù)。
下面展示四種查詢數(shù)據(jù)的方式:
//1
let fetchRequest1 = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedObjectContext)!
fetchRequest1.entity = entity
第一種方法通過默認(rèn)初始化NSFetchRequest,從 managedContext 來創(chuàng)建 Person 類的 EntityDescription,然后設(shè)置fetchRequest的entity來完成。
//2
let fetchRequest2 = NSFetchRequest(entityName: "Person")
第二種方法可以在初始化NSFetchRequest的時候傳入EntityName來完成,這是一種便捷的快速方法,在init的時候就制定了Entity。
//3
let fetchRequest3 = managedObjectModel.fetchRequestTemplateForName("peopleFR")
第三種通過調(diào)用managedObjectModel的.fetchRequestTemplateForName方法來獲取 NSFetchRequest。在Xcode的 Data Model Editor 界面中可以手動設(shè)置一些針對用戶需求常用的fetch屬性,可以使用這種方法來快速調(diào)用。
//4
let fetchRequest4 = managedObjectModel.fetchRequestFromTemplateWithName("peopleFR", substitutionVariables: ["NAME" :"Ray"])
第四種基于第三種,但是會在第三種的基礎(chǔ)上使用substitutionVariables進行再次的篩選。
在Editor上創(chuàng)建 Fetch Template
在 Data Model Editor 界面上長時間按住 Add Entity 的那個按鈕,選擇Add Fetch Request。
之后如果是查詢一個實體的全部數(shù)據(jù),就吧下拉框選為目標(biāo)查詢的實體。
幾個小點:
從 Editor 模板中取得 FetchRequest 的時候必須從 ManagedObjectModel 中取。
構(gòu)建的 CoreDataStack 類中只應(yīng)該有 ManagedContext 是 Public 的,其余的都應(yīng)該是 Private。
NSManagedObjectModel.fetchRequestTemplateForName()的參數(shù)必須跟 Editor 中的保持一致。
NSFetchRequest 神奇の存在
在 CoreData 框架中,NSFetchRequest 就像一把多功能的瑞士軍刀,你可以批量獲取數(shù)據(jù),可以獲取單個數(shù)據(jù),可以獲取最大最小、平均值等、
那么他是如何實現(xiàn)這些的呢,F(xiàn)R 有一個property叫做 resultType,默認(rèn)值是 NSManagedResultType:
NSManagedObjectResultType:默認(rèn)值,返回批量的 Managed Object 對象NSCountResultType: 類型如其名,返回 ManagedObjects.countNSDictionaryResultType: 返回不同的計算類型,稍后補充NSManagedObjectIDResultType: 返回特殊的標(biāo)記,而不是真實的對象,其實這個有點兒像 hashCode 的意思
NSCountResultType 實現(xiàn) count 計數(shù)
在獲取了 FR 之后,需要給 FR 添加 predicate,predicate 在創(chuàng)建的時候支持 key path,比如:
lazy var cheapVenuePredicate: NSPredicate = {
var predicate = NSPredicate(format: "priceInfo.priceCategory == %@", "$")
return predicate
}()
上面代碼中的 priceInfo.priceCategory 就是一個應(yīng)用。
func populateCheapVenueCountLabel() {
// $ fetch request
let fetchRequest = NSFetchRequest(entityName: "Venue")
fetchRequest.resultType = .CountResultType
fetchRequest.predicate = cheapVenuePredicate
do {
let results = try coreDataStack.context.executeFetchRequest(fetchRequest) as! [NSNumber]
let count = results.first!.integerValue
firstPriceCategoryLabel.text = "\(count) bubble tea places"
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
這個方法使用上面的 LazyLoading 的iVar - cheapVenuePredicate 作為 predicate,來做數(shù)據(jù)查詢。
這里指明了 fetchRequest.resultType = NSCountResultType,所以結(jié)果會返回一個包含了一個 NSNumber 的 NSArray。當(dāng)然這個 NSNumber 是一個 NSInteger,它就是那個count。
除了上面的一種執(zhí)行 executeFetchRequest 的方法獲取Count的方法之外,還可以直接調(diào)用 context 的countForFetchRequest 方法來獲取Count:
func populateExpensiveVenueCountLabel() {
// $$$ fetch request
let fetchRequest = NSFetchRequest(entityName: "Venue")
fetchRequest.resultType = .CountResultType
fetchRequest.predicate = expensiveVenuePredicate
var error: NSError?
let count = coreDataStack.context.countForFetchRequest(fetchRequest, error: &error)
if count != NSNotFound {
thirdPriceCategoryLabel.text = "\(count) bubble tea places"
} else {
print("Could not fetch \(error), \(error?.userInfo)")
}
}
上面的代碼中,使用的錯誤處理不是像之前使用的 try-catch 結(jié)構(gòu),而是使用 iOS SDK 中常見的 error 的結(jié)構(gòu),這是因為 countForFetchRequest 方法的第二個參數(shù)是一個 *NSError 類型,需要將一個 error 對象的指針傳遞進去。使用:
coreDataStack.context.countForFetchRequest(fetchRequest, error: &error)
來獲取查詢結(jié)果的 count。
DictionaryResultType 實現(xiàn)計算邏輯
前面說了,NSFetchRequest 可以左好多事情,這里包括了一些計算的邏輯。
對于某些需求,比如對查詢的結(jié)構(gòu)進行一些SUM、MIN、MAX這樣的常見操作,常見一些低效率的代碼把所有的數(shù)據(jù)全部查詢出來,然后在程序中使用 For 循環(huán)來進行篩選,這樣做又 Naive 又低效。
代碼如下:
func populateDealsCountLabel() {
//1 像之前一樣普通的創(chuàng)建 NSFetchRequest,但是 resultType 指定為 .DictionaryResultType
let fetchResult = NSFetchRequest(entityName: "Venue")
fetchResult.resultType = .DictionaryResultType
//2 創(chuàng)建一個 NSExpressionDescription 對象去請求 SUM,而且給它一個 name 標(biāo)示“sumDeals”,在resultResults中就會有這個 name 標(biāo)識的返回結(jié)果
let sumExpressionDesc = NSExpressionDescription()
sumExpressionDesc.name = "sumDeals"
//3 上面僅僅給了 ExpressionDescription 一個name標(biāo)示,但是只是一個String而已,真正讓它清楚自己要做sum求和需要給ExpressionDesc對象的這個 .expression 對象做配置:
//初始化一個 NSExpression 對象,function寫上“sum”,還有好多,使用 command 鍵按進去方法描述下面一大堆,后面的 argument 參數(shù)指明對查詢出來的結(jié)果的 specialCount 來進行邏輯計算。
sumExpressionDesc.expression = NSExpression(forFunction: "sum:", arguments: [NSExpression(forKeyPath: "specialCount")])
//指定計算結(jié)果的數(shù)據(jù)類型
sumExpressionDesc.expressionResultType = .Integer32AttributeType
//4 配置好了 NSExpressionDescription 對象之后,將配置好的它 set 為 NSFetchRequest 對象的 .propertiesToFetch(看見沒這里是ties,意思要接受數(shù)組,而且可以配置好多個,配置多個就返回多個嘍~)
fetchResult.propertiesToFetch = [sumExpressionDesc]
//5 司空見慣的查詢,看從 resultDic 中取得查詢結(jié)果的那個 [sumDeals] 就是前面 NSExpressDescription.name。
do {
let results = try coreDataStack.context.executeFetchRequest(fetchResult) as! [NSDictionary]
let resultDic = results.first!
let numDeals = resultDic["sumDeals"]
numDealsLabel.text = "\(numDeals!) total deals"
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
ManagedObjectIDResultType 查詢結(jié)果的唯一標(biāo)示
四種類型前面已經(jīng)說了三種,接下來的一種就是 ManagedObjectIDResultType,這種查詢類型會返回查詢結(jié)果的一個特殊標(biāo)志,一種 universal identifier 每一個 ManagedObject 對應(yīng)著一個特殊的 ID。
之前筆者認(rèn)為它像 hashCode 但是明顯又不一樣,因為即便是兩個內(nèi)容相同的 ManagedObjcet,他們的 ID 也都是不一樣的,但是 HashCode 確應(yīng)該是一樣的。
iOS 5 的時候取得 NSManagedObjectID 是線程安全的,但是現(xiàn)在已被棄用,但是有更好的并發(fā)模型提供給我們使用,以后會有 CoreData 與多線程并發(fā)。
另外提兩點:
NSFetchRequest 支持批量返回,可以通過設(shè)置
fetchBatchSize、fetchLimit和fetchOffset去配置 Batch 的 FetchRequest。另外可以通過
faulting來優(yōu)化內(nèi)存消耗:fault 是一中占位符,它代表著一個還沒有真正從存儲層取出到內(nèi)存的 ManagedObject。另外一重優(yōu)化內(nèi)存的方法就是使用 predicate,接下來會寫到。
(如果使用了 Editor 配置的 predicate,那么在 Runtime 的時候就不能更改 predicate。)
其實 NSPredicate 不屬于 Core Data 框架,它是屬于 Foundation 框架中的,更多有關(guān)于 NSPredicate 的,可以看 蘋果官方的文檔 。
FetchResults 排序
NSFetchRequest 另外一個神奇的功能是它能把獲取的數(shù)據(jù)進行排序,實現(xiàn)的機制是使用 NSSortDescription 類,而且這個排序的執(zhí)行時機是在 數(shù)據(jù)存儲層 也就是在 SQLite 層完成的,保證了排序的速度和效率。
案例:需要排序的模塊中加入 NSSortDescription 的 lazy Var 如下:
lazy var nameSortDescriptor: NSSortDescriptor = {
var sd = NSSortDescriptor(key: "name", ascending: true, selector: "localizedStandardCompare:")
return sd
}()
lazy var distanceSortDescription: NSSortDescriptor = {
var sd = NSSortDescriptor(key: "location.distance", ascending: true)
return sd
}()
第一個 NSSortDescription 按照姓名來進行排序,排序的比較方法選擇了 String 的 localizedStandardCompare:。
第二個 NSSortDescription 按照 location.distance 進行排序。
ascending 參數(shù)表示是否要升序,true -> ascending,false -> descending。
注意:
在 Core Data 框架之外,NSSortDescriptor 支持基于 Block Closure 的 Comparator,這里我們用的是一個 selector。其實在 Core Data 框架內(nèi)是不允許使用 Comparator 的方法來定義排序的。
同樣的,供給 Core Data 使用的 NSPredicate 也是不允許使用任何基于 Block 的 API。
上面兩點為什么呢?因為前面說了排序發(fā)生在數(shù)據(jù)存儲層,也就是在SQLite查詢的時候完成的,Block 的方式不能有效組成一個 SQLite 查詢語句。
localizedStandardCompare 是什么,當(dāng)你對用戶看到的那些字符串進行排序的時候,Apple 都建議傳遞 localizedStandardCompare來當(dāng)做排序的規(guī)則來對當(dāng)前字符串進行排序。
如果要某個 SortDescriptor 的逆向排序,可以調(diào)用它的 .reversedSordDescriptor取得。
nameSortDescriptor.reversedSortDescriptor as? NSSortDescriptor
配置好了 SortDescription 之后,將 NSFetchRequest 的 sortDescriptors 屬性設(shè)置為包含了 SortDescription 的數(shù)組:
fetchRequest.sortDescriptors = [sr]
異步Fetching(iOS 8 的特性)
如同很多其他 iOS 上的需求一樣,當(dāng)復(fù)雜且耗時長的工作放在主線程上,會造成線程阻塞,這個時候 UI 會處于一種假死的狀態(tài)。同樣的 Core Data 的 Fetch 也是一樣的,在 Fetch 條件復(fù)雜、數(shù)據(jù)量很大的情況下,同樣會造成線程阻塞。這個時候,這部分工作就應(yīng)該異步執(zhí)行。
Core Data 的異步執(zhí)行被封裝的相當(dāng)簡單,在已有的 FetchRequest 的基礎(chǔ)上,使用 NSAsynchronousFetchRequest 來實現(xiàn)異步請求。
NSAsynchronousFetchRequest 的命名容易讓人產(chǎn)生歧義,其實它并不是 FetchRequest 的 subclass,它跟 FetchRequest 一樣,同樣是 NSPersistentStoreRequest 的子類。也就是說實現(xiàn)異步的 Fetch 就是把 NSFetchRequest 替換為 NSAsynchronousFetchRequest。
首先在模塊原有的 FetchRequest 中模塊中添加 iVar 變量:
var asyncFetchRequest: NSAsynchronousFetchRequest!
NSAsynchronousFetchRequest 就像是已經(jīng)存在的 FetchRequest 的一個 Wrapper 一樣。創(chuàng)建一個 NSAsynchronousFetchRequest 需要一個正常的 FetchRequest 和一個 Completion Handle。
執(zhí)行請求的時候類似,不過請求的方法從 executeFetchRequest 變成了 executeRequest,傳遞進去的參數(shù)也從 FetchRequest 變成了 AsynchronousFetchRequest。
執(zhí)行請求之后,返回的數(shù)據(jù)為:NSAsynchronousFetchResult。
調(diào)用的方法如下:
fetchRequest = NSFetchRequest(entityName: "Venue")
asyncFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) {
//查詢成功的處理
[unowned self] (result: NSAsynchronousFetchResult!) -> Void in
self.venues = result.finalResult as! [Venue]
self.tableView.reloadData()
}
do {
try coreDataStack.context.executeRequest(asyncFetchRequest)
//Returns immediately, cancel here if you want.
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
block 之內(nèi)是對返回的數(shù)據(jù)做處理。
另外,如果要取消正在,asyncFetchRequest 可以調(diào)用 cancel() 方法來取消這次異步請求。
批量更新(不做查詢,直接在Store層進行更新,iOS 8 的特性)
舉例一種情況,如果我們要對十萬條數(shù)據(jù)都進行同樣的一個屬性的更新,一般的做法我們需要取出這十萬條數(shù)據(jù),我們?nèi)〕鍪嗳f條數(shù)據(jù)只是為了做一個很簡單的更新。
經(jīng)典的案例是電子郵箱類似的APP中 標(biāo)記所有郵件為已讀 這樣的需求,難道要把上千封郵件全部請求出來嗎,顯然不是。
iOS 8 發(fā)布了支持 批量更新(Bench Update) 的 NSBatchUpdateRequest,使用它可以在不做查詢的情況下更新數(shù)據(jù)。
NSBatchUpdateRequest 實現(xiàn)的原理是完全繞開了 NSManagedObjectContext,直接去 NSPersistentStore 層去做 Update。
//這四行代碼,在初始化的時候配置好EntityName, 配置影響的property和更改的值 以及 配置影響的Store,以及返回Result的數(shù)據(jù)類型。
//創(chuàng)建NSBatchUpdateRequest 的實例,entityName 作為初始化參數(shù)。
let batchUpdate = NSBatchUpdateRequest(entityName: "myEntityName")
//標(biāo)明需要 Update 的 property 和 值
batchUpdate.propertiesToUpdate = ["favorite" : NSNumber(bool: true)]
//被影響的Stores 默認(rèn)情況下這么寫就可以,如果涉及比較多的PersistentStores 情況就更復(fù)雜了。
batchUpdate.affectedStores = coreDataStack.context.persistentStoreCoordinator!.persistentStores
//配置返回數(shù)據(jù)的類型,還可以是 UpdatedObjectIDsResultType。
batchUpdate.resultType = .UpdatedObjectsCountResultType
//執(zhí)行批量更新
do {
let batchResult = try coreDataStack.context.executeRequest(batchUpdate) as! NSBatchUpdateResult
print("Records updated \(batchResult.result!)")
} catch let error as NSError {
print("Could not update \(error), \(error.userInfo)")
}
在初始化的時候配置好EntityName, 配置影響的property和更改的值 以及 配置影響的Store,以及返回Result的數(shù)據(jù)類型。
另外提一下:如同這種在 Store 層的數(shù)據(jù)更新,數(shù)據(jù)刪除也有同樣的API,在 iOS 9 的時候蘋果提供了一個類似 NSBatchUpdateRequest 的類來做在 Store 層的數(shù)據(jù)刪除,使用 ---- NSBatchDeleteRequest,同樣它們兩個都是 NSPersistentStoreRequest 的子類。
注意:
額外需要注意的一點是,前面提到了「NSBatchUpdateRequest 實現(xiàn)的原理是完全繞開了 NSManagedObjectContext,直接去 NSPersistentStore 層去做 Update?!?/p>
所以做了批量 Update / Delete 之后,你之前請求的那部分?jǐn)?shù)據(jù)已經(jīng)失效了,因為它們跟數(shù)據(jù)庫已經(jīng)失去了同步性。
End.