Core Data learning note with Swift.

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)建了屬于自定義的EntityManaged 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其實包含著NSEntityDescriptionNSPropertyDescription,前者相當(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 ObjectContext,那么在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、NSPersistentStoreCoordinatorNSManagedObjectModel

 //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 獲取。

而所有的NSManagedObjectModelNSPersistentStore都可以通過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方法,這里就是在applicationDidEnterBackgroundapplicationWillTerminate代理方法中調(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.count

  • NSDictionaryResultType: 返回不同的計算類型,稍后補充

  • 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é)果會返回一個包含了一個 NSNumberNSArray。當(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、fetchLimitfetchOffset 去配置 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.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容