Core Data learning note with Swift.

CoreData Learning note

后续阅读:

https://www.objc.io/issue-4/core-data-overview.html
中文:http://www.cocoachina.com/ios/20130911/6981.html

01: 存取:

首先开头用最简单的小例子。

//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来存储图像,但是巨大的图像直接存储在数据库中会导致性能降低,选择类型为BD的Attribute,然后可以在右边的属性选择框中勾选Allows External Storage,类似所以存储在外部,提高数据访问性能。

Transformable类型用于存储那些实现了NSCoding protocol 的对象,比如 UIColor,UIColor 实现了NSSecureCoding

03. 不要过于依赖 NSManagedObject

虽然使用KVC很便捷,但是尽量不要过于依赖KVC。

04.自动创建Entity的子类:

打开。xcfatamodeld文件 -> Editor -> Create NSManagedObject Subclass

创建出来的每个 Entity 对应了两个文件,普通的文件仅仅包含了所有的 action 操作,Entity 的 所有属性都用 extension 的方式进行了实现。所有的属性在Extension中声明的时候前面都加上了@NSManaged,这个标示通知了编译器,这个 property 会在runtime的时候提供,而不是在编译的时候。

一般的模式下,property 是由内存中的实例变量实现的,但是在managedObject中,是由managed object context实现的,compile time 并不知道。

创建了属于自定义的EntityManaged Object的子类有两个好处:
· 隔离的KVC,编译器可以获取 Property,而不是通过 KVC 的方式,方便编写程序。
· 方便重写方法。当时注意Apple文档中标出了一些从来都不应该重写的方法。

05. 结合子类 NSManagedObject 去实现添加和 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,然后右边可以设置该项属性的最大值、最小值和默认值。

07. 具体的操作对象:

具体这一部分可以添加常用的方法:会在后续添加。

http://justsee.iteye.com/blog/1881110

NSManagedObjectContext

表示被操作数据的上下文环境。类似于一种持续性的数据库连接,可以做增删查等操作。

而且只有在调用Save方法的时候,这些所有的改动才会被提交,否则是不会在持久层做任何改动的。

支持撤销和重做。

NSManagedObjectModel

表示被管理的数据模型,这里包含着所有对象的表格信息,所有数据结构,包括他们之间的关系。

在这里添加实体的属性,添加实体和实体之间的关系。

所以NSManagedObjectModel其实包含着NSEntityDescriptionNSPropertyDescription,前者相当于数据库中的一个表,后者相当于表中的一列。NSPropertyDescription可以描述实体的基本属性(Attributes)、实体之间的关系(Relationships)还有 查询属性(FetchedProperty)。

查询属性对应NSFetchedPropertyDescription对象。

Persistent Store

持久化存储层,是由文件或者外部数据库组成的,大多数情况下访问持久化层全部由上下文Context来完成。

CoreData 提供了四种持久化层的方案:

非原子访问的:NSQLiteStoreType
原子访问的:BSXMLStoreTypeNSBinaryStoreTypeNSInMemoryStoreType

XML是将数据存为XML文件,只在 OS X 平台下可用。Binary的方法是存为一个Data文件。InMemory的方式是不会对数据进行真正意义上的持久化,全部存储在内存中,当应用程序退出时,数据也就消失了。

NSPersistentStoreCoordinator

持久化存储助理的存在相当于和数据持久层的连接器,SQLite的话就是和数据库的连接,它设置着数据库的路径名字、位置、存储方式和存储的时机。
CoreData其实就相当于一个栈,栈的底层是持久化存储层,栈顶是Context,所以一般简单的需求我们只需要使用栈顶的Context,那么NSPersistentStoreCoordinator就是栈中层的一层存在,协调上栈的上下层关系。

负责理解NSManagedObjectModel和去NSPersistentStore中执行相应操作。

补充NSManagedObjectContext

NSManagedObjectContext就像内存中的便签纸,临时修改你的ManagedObject,所以知道你提交save(),否则持久化层是不会有变化的。

  • 上下文管理这Managed Object的生命周期,管理的同时提供了很多高级特征可以给以使用,比如排序,Validation、关系管理等。

  • 一个Managed Object是不能独立于Context存在的,即有Managed Object就一定会有它的Context,也可以通过 Managed Object 的.managedObjectContext属性取得它的Context对象。

  • 一旦设置了Managed ObjectContext,那么在Object的很长的生命周期中就会一直跟这个特定的Context关联。

  • 一个应用程序可以有很多个Context,你可以创建两个Context指向一些相同的PersistentStore持久化层。

  • Context并不是线程安全的,所以对于Managed Object也是一样的,它们只可以在创建他们的那个线程上进行操作。

08. 创建自己的线程栈

创建:CoreDataStack.swift

第一部分创建 Model 的名字和 Document 的路径URL:
创建新的 .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]
  }()
}

第二步给类添加懒加载的三个属性 NSManagedObjectContextNSPersistentStoreCoordinatorNSManagedObjectModel

 //NSManagedObjectContext
 //Note: ConcurrencyType 的具体参数会在后面补充添加,暂时先使用 .MainQueueConcurrencyType
 //创建出来Context是完全没有意义的,直到设置了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图形化设计Entity和Relationship的那个文件,使用它来创建ManagedObjectModel
 private lazy var managedObjectModel: NSManagedObjectModel = {
   let modelURL = NSBundle.mainBundle().URLForResource(self.modelName, withExtension: "momd")!
   return NSManagedObjectModel(contentsOfURL: modelURL)!
 }()

添加的每 Property 都对应着 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

接下来在合适的时机调用SaveContent方法,这里就是在applicationDidEnterBackgroundapplicationWillTerminate代理方法中调用:

coreDataStack.saveContent()

到这里,一个CoreData的栈类就创建完成了,并且已经实现了在应用退出的时候对 Context 进行 saveing 的操作


09.高级查询

简单的查询通过创建 NSFetchRequest 来从 CoreData 中取得数据。

下面展示四种查询数据的方式:

//1
let fetchRequest1 = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedObjectContext)! 
fetchRequest1.entity = entity

第一种方法通过默认初始化NSFetchRequest,从 managedContext 来创建 Person 类的 EntityDescription,然后设置fetchRequest的entity来完成。

//2
let fetchRequest2 = NSFetchRequest(entityName: "Person")

第二种方法可以在初始化NSFetchRequest的时候传入EntityName来完成,这是一种便捷的快速方法,在init的时候就制定了Entity。

//3
let fetchRequest3 = managedObjectModel.fetchRequestTemplateForName("peopleFR")

第三种通过调用managedObjectModel.fetchRequestTemplateForName方法来获取 NSFetchRequest。在Xcode的 Data Model Editor 界面中可以手动设置一些针对用户需求常用的fetch属性,可以使用这种方法来快速调用。

//4
let fetchRequest4 = managedObjectModel.fetchRequestFromTemplateWithName("peopleFR", substitutionVariables: ["NAME" :"Ray"])

第四种基于第三种,但是会在第三种的基础上使用substitutionVariables进行再次的筛选。


在Editor上创建 Fetch Template

在 Data Model Editor 界面上长时间按住 Add Entity 的那个按钮,选择Add Fetch Request

之后如果是查询一个实体的全部数据,就吧下拉框选为目标查询的实体。

几个小点:

  • 从 Editor 模板中取得 FetchRequest 的时候必须从 ManagedObjectModel 中取。

  • 构建的 CoreDataStack 类中只应该有 ManagedContext 是 Public 的,其余的都应该是 Private。

  • NSManagedObjectModel.fetchRequestTemplateForName()的参数必须跟 Editor 中的保持一致。

NSFetchRequest 神奇の存在

在 CoreData 框架中,NSFetchRequest 就像一把多功能的瑞士军刀,你可以批量获取数据,可以获取单个数据,可以获取最大最小、平均值等、

那么他是如何实现这些的呢,FR 有一个property叫做 resultType,默认值是 NSManagedResultType

  • NSManagedObjectResultType:默认值,返回批量的 Managed Object 对象

  • NSCountResultType: 类型如其名,返回 ManagedObjects.count

  • NSDictionaryResultType: 返回不同的计算类型,稍后补充

  • NSManagedObjectIDResultType: 返回特殊的标记,而不是真实的对象,其实这个有点儿像 hashCode 的意思

NSCountResultType 实现 count 计数

在获取了 FR 之后,需要给 FR 添加 predicate,predicate 在创建的时候支持 key path,比如:

lazy var cheapVenuePredicate: NSPredicate = {
  var predicate = NSPredicate(format: "priceInfo.priceCategory == %@", "$")
  return predicate
}()

上面代码中的 priceInfo.priceCategory 就是一个应用。

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,来做数据查询。

这里指明了 fetchRequest.resultType = NSCountResultType,所以结果会返回一个包含了一个 NSNumberNSArray。当然这个 NSNumber 是一个 NSInteger,它就是那个count。

除了上面的一种执行 executeFetchRequest 的方法获取Count的方法之外,还可以直接调用 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 结构,而是使用 iOS SDK 中常见的 error 的结构,这是因为 countForFetchRequest 方法的第二个参数是一个 *NSError 类型,需要将一个 error 对象的指针传递进去。使用:

coreDataStack.context.countForFetchRequest(fetchRequest, error: &error)

来获取查询结果的 count。

DictionaryResultType 实现计算逻辑

前面说了,NSFetchRequest 可以左好多事情,这里包括了一些计算的逻辑。

对于某些需求,比如对查询的结构进行一些SUM、MIN、MAX这样的常见操作,常见一些低效率的代码把所有的数据全部查询出来,然后在程序中使用 For 循环来进行筛选,这样做又 Naive 又低效。

代码如下:

func populateDealsCountLabel() {
  //1 像之前一样普通的创建 NSFetchRequest,但是 resultType 指定为 .DictionaryResultType
  let fetchResult = NSFetchRequest(entityName: "Venue")
  fetchResult.resultType = .DictionaryResultType
  
  //2 创建一个 NSExpressionDescription 对象去请求 SUM,而且给它一个 name 标示“sumDeals”,在resultResults中就会有这个 name 标识的返回结果
  let sumExpressionDesc = NSExpressionDescription()
  sumExpressionDesc.name = "sumDeals"
  
  //3 上面仅仅给了 ExpressionDescription 一个name标示,但是只是一个String而已,真正让它清楚自己要做sum求和需要给ExpressionDesc对象的这个 .expression 对象做配置:
  //初始化一个 NSExpression 对象,function写上“sum”,还有好多,使用 command 键按进去方法描述下面一大堆,后面的 argument 参数指明对查询出来的结果的 specialCount 来进行逻辑计算。
  sumExpressionDesc.expression = NSExpression(forFunction: "sum:", arguments: [NSExpression(forKeyPath: "specialCount")])
  //指定计算结果的数据类型
  sumExpressionDesc.expressionResultType = .Integer32AttributeType
  
  //4 配置好了 NSExpressionDescription 对象之后,将配置好的它 set 为 NSFetchRequest 对象的 .propertiesToFetch(看见没这里是ties,意思要接受数组,而且可以配置好多个,配置多个就返回多个喽~)
  fetchResult.propertiesToFetch = [sumExpressionDesc]
  
  //5 司空见惯的查询,看从 resultDic 中取得查询结果的那个 [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 查询结果的唯一标示

四种类型前面已经说了三种,接下来的一种就是 ManagedObjectIDResultType,这种查询类型会返回查询结果的一个特殊标志,一种 universal identifier 每一个 ManagedObject 对应着一个特殊的 ID。

之前笔者认为它像 hashCode 但是明显又不一样,因为即便是两个内容相同的 ManagedObjcet,他们的 ID 也都是不一样的,但是 HashCode 确应该是一样的。

iOS 5 的时候取得 NSManagedObjectID 是线程安全的,但是现在已被弃用,但是有更好的并发模型提供给我们使用,以后会有 CoreData 与多线程并发。

另外提两点:

  • NSFetchRequest 支持批量返回,可以通过设置 fetchBatchSizefetchLimitfetchOffset 去配置 Batch 的 FetchRequest。

  • 另外可以通过 faulting 来优化内存消耗:fault 是一中占位符,它代表着一个还没有真正从存储层取出到内存的 ManagedObject。

  • 另外一重优化内存的方法就是使用 predicate,接下来会写到。

(如果使用了 Editor 配置的 predicate,那么在 Runtime 的时候就不能更改 predicate。)

其实 NSPredicate 不属于 Core Data 框架,它是属于 Foundation 框架中的,更多有关于 NSPredicate 的,可以看 苹果官方的文档

FetchResults 排序

NSFetchRequest 另外一个神奇的功能是它能把获取的数据进行排序,实现的机制是使用 NSSortDescription 类,而且这个排序的执行时机是在 数据存储层 也就是在 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 参数表示是否要升序,true -> ascendingfalse -> descending

注意:

  • 在 Core Data 框架之外,NSSortDescriptor 支持基于 Block Closure 的 Comparator,这里我们用的是一个 selector。其实在 Core Data 框架内是不允许使用 Comparator 的方法来定义排序的。

  • 同样的,供给 Core Data 使用的 NSPredicate 也是不允许使用任何基于 Block 的 API。

  • 上面两点为什么呢?因为前面说了排序发生在数据存储层,也就是在SQLite查询的时候完成的,Block 的方式不能有效组成一个 SQLite 查询语句。

localizedStandardCompare 是什么,当你对用户看到的那些字符串进行排序的时候,Apple 都建议传递 localizedStandardCompare来当做排序的规则来对当前字符串进行排序。

如果要某个 SortDescriptor 的逆向排序,可以调用它的 .reversedSordDescriptor取得。

nameSortDescriptor.reversedSortDescriptor as? NSSortDescriptor

配置好了 SortDescription 之后,将 NSFetchRequest 的 sortDescriptors 属性设置为包含了 SortDescription 的数组:

fetchRequest.sortDescriptors = [sr]

异步Fetching(iOS 8 的特性)

如同很多其他 iOS 上的需求一样,当复杂且耗时长的工作放在主线程上,会造成线程阻塞,这个时候 UI 会处于一种假死的状态。同样的 Core Data 的 Fetch 也是一样的,在 Fetch 条件复杂、数据量很大的情况下,同样会造成线程阻塞。这个时候,这部分工作就应该异步执行。

Core Data 的异步执行被封装的相当简单,在已有的 FetchRequest 的基础上,使用 NSAsynchronousFetchRequest 来实现异步请求。

NSAsynchronousFetchRequest 的命名容易让人产生歧义,其实它并不是 FetchRequest 的 subclass,它跟 FetchRequest 一样,同样是 NSPersistentStoreRequest 的子类。也就是说实现异步的 Fetch 就是把 NSFetchRequest 替换为 NSAsynchronousFetchRequest

首先在模块原有的 FetchRequest 中模块中添加 iVar 变量:

var asyncFetchRequest: NSAsynchronousFetchRequest!

NSAsynchronousFetchRequest 就像是已经存在的 FetchRequest 的一个 Wrapper 一样。创建一个 NSAsynchronousFetchRequest 需要一个正常的 FetchRequest 和一个 Completion Handle。

执行请求的时候类似,不过请求的方法从 executeFetchRequest 变成了 executeRequest,传递进去的参数也从 FetchRequest 变成了 AsynchronousFetchRequest。

执行请求之后,返回的数据为:NSAsynchronousFetchResult

调用的方法如下:

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 之内是对返回的数据做处理。

另外,如果要取消正在,asyncFetchRequest 可以调用 cancel() 方法来取消这次异步请求。

批量更新(不做查询,直接在Store层进行更新,iOS 8 的特性)

举例一种情况,如果我们要对十万条数据都进行同样的一个属性的更新,一般的做法我们需要取出这十万条数据,我们取出十多万条数据只是为了做一个很简单的更新。

经典的案例是电子邮箱类似的APP中 标记所有邮件为已读 这样的需求,难道要把上千封邮件全部请求出来吗,显然不是。

iOS 8 发布了支持 批量更新(Bench Update)NSBatchUpdateRequest,使用它可以在不做查询的情况下更新数据。

NSBatchUpdateRequest 实现的原理是完全绕开了 NSManagedObjectContext,直接去 NSPersistentStore 层去做 Update。

//这四行代码,在初始化的时候配置好EntityName, 配置影响的property和更改的值 以及 配置影响的Store,以及返回Result的数据类型。
//创建NSBatchUpdateRequest 的实例,entityName 作为初始化参数。
let batchUpdate = NSBatchUpdateRequest(entityName: "myEntityName")
//标明需要 Update 的 property 和 值
batchUpdate.propertiesToUpdate = ["favorite" : NSNumber(bool: true)]
//被影响的Stores 默认情况下这么写就可以,如果涉及比较多的PersistentStores 情况就更复杂了。
batchUpdate.affectedStores = coreDataStack.context.persistentStoreCoordinator!.persistentStores
//配置返回数据的类型,还可以是 UpdatedObjectIDsResultType。
batchUpdate.resultType = .UpdatedObjectsCountResultType

//执行批量更新
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的数据类型。

另外提一下:如同这种在 Store 层的数据更新,数据删除也有同样的API,在 iOS 9 的时候苹果提供了一个类似 NSBatchUpdateRequest 的类来做在 Store 层的数据删除,使用 ---- NSBatchDeleteRequest,同样它们两个都是 NSPersistentStoreRequest 的子类。

注意:

额外需要注意的一点是,前面提到了「NSBatchUpdateRequest 实现的原理是完全绕开了 NSManagedObjectContext,直接去 NSPersistentStore 层去做 Update。」

所以做了批量 Update / Delete 之后,你之前请求的那部分数据已经失效了,因为它们跟数据库已经失去了同步性。

End.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,468评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,620评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,427评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,160评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,197评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,334评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,775评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,444评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,628评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,459评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,508评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,210评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,767评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,850评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,076评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,627评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,196评论 2 341

推荐阅读更多精彩内容