我使用Core Data在本地持久化Web服务调用的结果。web服务返回完整的对象模型,比如说,“Cars”——可能有2000辆车(我不能让web服务返回小于1辆或ALL辆的任何东西。

下次打开应用程序时,我希望通过再次调用所有汽车的Web服务来刷新Core Data持久化副本,但是为了防止重复,我需要首先清除本地缓存中的所有数据。

是否有一种更快的方法来清除管理对象上下文中特定实体的所有实例(例如,类型为“CAR”的所有实体),或者我需要查询它们调用,然后遍历结果删除每个,然后保存?

理想情况下,我可以说删除实体所在的所有地方等等。


当前回答

iOS 11.3和Swift 4.1

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
        let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest )
        batchDeleteRequest.resultType = .resultTypeCount
        do {
            let batchDeleteResult = try dataController.viewContext.execute(batchDeleteRequest) as! NSBatchDeleteResult
            print("The batch delete request has deleted \(batchDeleteResult.result!) records.")
            dataController.viewContext.reset() // reset managed object context (need it for working)
        } catch {
            let updateError = error as NSError
            print("\(updateError), \(updateError.userInfo)")
        }

你必须在执行之后调用重置。如果不是,它将不会在表视图上更新。

其他回答

这是一个类似的问题,有人建议设置一个关系删除规则,这样你只需要删除一个对象。因此,如果您已经或可以创建一个与汽车具有多对关系的实体,并将删除规则设置为级联,当您删除更高的实体时,所有的汽车也将被删除。这可以节省一些处理时间,因为您不必执行加载所有汽车的步骤。在更大的数据集中,这可能是绝对必要的。

对于Swift 2.0:

class func clearCoreData(entity:String) {
  let fetchRequest = NSFetchRequest()
  fetchRequest.entity = NSEntityDescription.entityForName(entity, inManagedObjectContext: moc!)
  fetchRequest.includesPropertyValues = false
  do {
    if let results = try moc!.executeFetchRequest(fetchRequest) as? [NSManagedObject] {
      for result in results {
        moc!.deleteObject(result)
      }

      try moc!.save()
    }
  } catch {
    LOG.debug("failed to clear core data")
  }
}

Swift 4, iOS 10+ 静态函数,可以应用于任何实体删除其所有数据

protocol NSManagedObjectHelper {
}
extension NSManagedObject: NSManagedObjectHelper {
}
extension NSManagedObjectHelper where Self: NSManagedObject {
    static func removeAllObjectsInContext(_ managedContext: NSManagedObjectContext) {
        let request: NSFetchRequest = NSFetchRequest(entityName: String(describing: self))
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
        do {
            deleteRequest.resultType = .resultTypeObjectIDs//to clear objects from memory
            let result = try managedContext.execute(deleteRequest) as? NSBatchDeleteResult
            if let objectIDArray = result?.result as? [NSManagedObjectID] {
                let changes = [NSDeletedObjectsKey : objectIDArray]
                /*By calling mergeChangesFromRemoteContextSave, all of the NSManagedObjectContext instances that are referenced will be notified that the list of entities referenced with the NSManagedObjectID array have been deleted and that the objects in memory are stale. This causes the referenced NSManagedObjectContext instances to remove any objects in memory that are loaded which match the NSManagedObjectID instances in the array.*/
                NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [managedContext])
            }
            try managedContext.save()
        } catch let error {
            print(error)
        }
    }
}

“房间”是一个实体

Room.removeAllObjectsInContext(self.persistentContainer.viewContext)

在20191025编辑:如果我们在同一个项目中使用多个目标,"Self.fetchRequest()"指令可能会导致问题。所以替换为NSFetchRequest(entityName: String(description: self))

这段代码适用于iOS 9及以下版本

class func deleteAllRecords(in entity : String) // entity = Your_Entity_Name
    {

        let context = CoreDataStack.getContext() // Note:- Replace your context here with CoreDataStack.getContext()
        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
        if #available(iOS 9, *)
        {
            let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
            do
            {
                try context.execute(deleteRequest)
                try context.save()
            }
            catch
            {
                print("There was an error:\(error)")
            }
        }
        else
        {
            do{
                let deleteRequest = try context.fetch(deleteFetch)
                for anItem in deleteRequest {
                    context.delete(anItem as! NSManagedObject)
                }
            }
            catch
            {
                print("There was an error:\(error)")
            }
        }
        CoreDataStack.saveContext() // Note:- Replace your savecontext here with CoreDataStack.saveContext()
    }

NSBatchDeleteRequest Swift 5.5和Xcode 13.2

删除SQLite持久存储中的对象,而不加载到内存中。在持久存储上执行的更改不会反映在当前内存中的对象中。

执行批量删除之后,从持久存储中删除内存中的所有对象。

下面是执行batchDeleteRequest的一些方便的扩展方法

extension NSManagedObject {
    
    private static var entityName: String {
        return String(describing: self)
    }
    
    static func fetchRequest<Self>(
        with predicate: NSPredicate? = nil,
        configureBlock: ((NSFetchRequest<Self>) -> Void)? = nil
    ) -> NSFetchRequest<Self> where Self: NSFetchRequestResult {
        let request = NSFetchRequest<Self>(entityName: entityName)
        request.predicate = predicate
        configureBlock?(request)
        return request
    }
    
    static func batchDelete(with fetchRequest: NSFetchRequest<NSFetchRequestResult>,
                            in context: NSManagedObjectContext) {
        let batchDeteleRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
        batchDeteleRequest.resultType = .resultTypeObjectIDs
        do {
            if let fetchResult = try context.execute(batchDeteleRequest) as? NSBatchDeleteResult,
               let deletedManagedObjectIds = fetchResult.result as? [NSManagedObjectID], !deletedManagedObjectIds.isEmpty {
                let changes = [NSDeletedObjectsKey: deletedManagedObjectIds]
                NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [context])
            }
        } catch {
            print("Error while executing batchDeleteRequest: \(error.localizedDescription)")
        }
    }
}

示例:我们可以通过传递fetchRequest调用batchDelete静态方法批量删除实体

class Entity: NSManagedObject {
    @NSManaged var name: String?
    @NSManaged var value: String?
}

extension Entity {
    
    static func executeBatchDelete(in context: NSManagedObjectContext) {
        let predicate = NSPredicate(format: "\(#keyPath(Entity.value)) == %@)", "abc")
        let deleteRequest: NSFetchRequest<NSFetchRequestResult> = Entity.fetchRequest(with: predicate)
        Entity.batchDelete(with: deleteRequest, in: context)
    }
}