核心数据:删除实体所有实例的最快方法


383

我正在使用Core Data在本地保留Web服务调用的结果。Web服务为“汽车”返回完整的对象模型-大概有2000辆(而我不能让Web服务返回少于1辆或所有汽车。

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

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

理想情况下,我只能说删除实体为Blah的所有内容。


您可以使用内存数据库
J. Doe,

Answers:


718

iOS 9及更高版本:

iOS 9添加了一个名为的新类NSBatchDeleteRequest,该类使您可以轻松删除与谓词匹配的对象,而不必将它们全部加载到内存中。使用方法如下:

迅捷5

let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

do {
    try myPersistentStoreCoordinator.execute(deleteRequest, with: myContext)
} catch let error as NSError {
    // TODO: handle the error
}

目标C

NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Car"];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];

NSError *deleteError = nil;
[myPersistentStoreCoordinator executeRequest:delete withContext:myContext error:&deleteError];

有关批处理删除的更多信息,请参见WWDC 2015的“核心数据新增功能”会话(从〜14:10开始)。

iOS 8及更早版本:

全部获取并删除所有内容:

NSFetchRequest *allCars = [[NSFetchRequest alloc] init];
[allCars setEntity:[NSEntityDescription entityForName:@"Car" inManagedObjectContext:myContext]];
[allCars setIncludesPropertyValues:NO]; //only fetch the managedObjectID

NSError *error = nil;
NSArray *cars = [myContext executeFetchRequest:allCars error:&error];
[allCars release];
//error handling goes here
for (NSManagedObject *car in cars) {
  [myContext deleteObject:car];
}
NSError *saveError = nil;
[myContext save:&saveError];
//more error handling here

74
我还将配置提取仅检索NSManagedObjectID,以减少加载整个对象结构中的任何开销。
Marcus S. Zarra

38
使用[allCars setIncludesPropertyValues:NO];仅获取NSMangagedObjectID并不明显。(也不必费心寻找如何为对象ID制作NSPropertyDescription!)
ohhorob

6
对不起,对于新手问题:for循环结束后是否需要保存上下文?例如[myContext save];
史蒂夫

6
核心数据中是否有任何新工具可以提高效率?对于我的应用程序来说,这已经是一个严重的问题,因为它已经无法移植到Core Data。从几张表之一删除所有4000个条目需要花费几秒钟的时间。用户等待的时间太长。直接与sqlite进行相同的请求似乎是瞬时的。
David

4
@DaveDeLong NSBatchDeleteRequest如何触发NSFetchedResultsController委托?我几乎尝试了所有事情,但没有任何反应。
Foriger

36

Swift 3中重置实体:

func resetAllRecords(in entity : String) // entity = Your_Entity_Name
    {

        let context = ( UIApplication.shared.delegate as! AppDelegate ).persistentContainer.viewContext
        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
        do
        {
            try context.execute(deleteRequest)
            try context.save()
        }
        catch
        {
            print ("There was an error")
        }
    }

32

更加干净通用:添加此方法:

- (void)deleteAllEntities:(NSString *)nameEntity
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:nameEntity];
    [fetchRequest setIncludesPropertyValues:NO]; //only fetch the managedObjectID

    NSError *error;
    NSArray *fetchedObjects = [theContext executeFetchRequest:fetchRequest error:&error];
    for (NSManagedObject *object in fetchedObjects)
    {
        [theContext deleteObject:object];
    }

    error = nil;
    [theContext save:&error];
}

16

对于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")
  }
}

12

迅速:

let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName(entityName, inManagedObjectContext: context)
fetchRequest.includesPropertyValues = false

var error:NSError?
if let results = context.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject] {
    for result in results {
        context.deleteObject(result)
    }

    var error:NSError?
    if context.save(&error) {
        // do something after save

    } else if let error = error {
        println(error.userInfo)
    }

} else if let error = error {
    println("error: \(error)")
}

1
这个答案应与新的try / catch错误处理更新
Suragch

10

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


1
我刚刚在我目前的项目中使用了大约600个核心数据对象进行了尝试。当我用级联将它们封装在另一个对象中时,删除它大约花了9.1秒。如果我使用Dave建议的方法,则删除大约需要8.7秒。对我来说不是一个明显的区别。
安德鲁·齐默

8

已经发布了一个很好的答案,这只是一个建议!

一个好方法是将一个类别添加到 NSManagedObject并实现一个像我一样的方法:

头文件(例如NSManagedObject+Ext.h

@interface NSManagedObject (Logic)

+ (void) deleteAllFromEntity:(NSString*) entityName;

@end

代码文件:(例如NSManagedObject + Ext.m)

@implementation NSManagedObject (Logic)

+ (void) deleteAllFromEntity:(NSString *)entityName {
    NSManagedObjectContext *managedObjectContext = [AppDelegate managedObjectContext];
    NSFetchRequest * allRecords = [[NSFetchRequest alloc] init];
    [allRecords setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext]];
    [allRecords setIncludesPropertyValues:NO];
    NSError * error = nil;
    NSArray * result = [managedObjectContext executeFetchRequest:allRecords error:&error];
    for (NSManagedObject * profile in result) {
        [managedObjectContext deleteObject:profile];
    }
    NSError *saveError = nil;
    [managedObjectContext save:&saveError];
}

@end

...您唯一需要做的就是从应用程序委托中获取managedObjectContext,或者将其存放在其中;)

之后,您可以像这样使用它:

[NSManagedObject deleteAllFromEntity:@"EntityName"];

进一步的优化可能是删除tha实体名称的参数,然后从clazzname中获取名称。这将导致用法:

[ClazzName deleteAllFromEntity];

更干净的隐式(作为NSManagedObjectContext的类别):

@implementation NSManagedObjectContext (Logic)

- (void) deleteAllFromEntity:(NSString *)entityName {
    NSFetchRequest * allRecords = [[NSFetchRequest alloc] init];
    [allRecords setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:self]];
    [allRecords setIncludesPropertyValues:NO];
    NSError * error = nil;
    NSArray * result = [self executeFetchRequest:allRecords error:&error];
    for (NSManagedObject * profile in result) {
        [self deleteObject:profile];
    }
    NSError *saveError = nil;
    [self save:&saveError];
}

@end

用法如下:

[managedObjectContext deleteAllFromEntity:@"EntityName"];

1
抱歉,但[AppDelegate managedObjectContext]不一定是“干净的体系结构”。;-)
Daniel Rinser 2015年

好的,是的。上面的代码基于一个managedObjectContext。最主要的一个;)在多线程代码中,我通常将应用程序委托的主要MOC合并到其他人
Erhard Dinhobl

1
@DanielRinser可以是deleteAllFromEntity: inManagedObjectContext:
Mohamed Elkassas

是。最好将deleteAllFromEntity方法从类方法更改为对象方法。那么您可以直接在MOC实例上调用deleteAllFromEntity。
Erhard Dinhobl,2016年

7

Swift 4,iOS 12和Xcode 10更新

100%工作只是剪切和粘贴

只需将此函数放在相关的类中,然后在函数或按钮的任何位置或任何位置或任何位置调用此函数self.deleteData()viewDidLoad()以便通过单击按钮来删除实体中的所有数据,并将“ myEntity”替换为您在实体中定义的实体核心数据

func deleteData() {
    let appDel:AppDelegate = (UIApplication.shared.delegate as! AppDelegate)
    let context:NSManagedObjectContext = appDel.persistentContainer.viewContext
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "myEntity")
    fetchRequest.returnsObjectsAsFaults = false         
    do {
        let results = try context.fetch(fetchRequest)
        for managedObject in results {
            if let managedObjectData: NSManagedObject = managedObject as? NSManagedObject {
                context.delete(managedObjectData)
            }
        }
    } catch let error as NSError {
        print("Deleted all my data in myEntity error : \(error) \(error.userInfo)")
    }
}

谢谢,但是为什么NSBatchDeleteRequest概念不起作用?任何的想法。
Suresh Durishetti

@SureshDurishetti您是否在课堂上导入了CoreData?
Xcodian Solangi

1
是的,添加了CoreDate。但是没有运气。
Suresh Durishetti

4
您忘了在上下文上添加通话保存,添加context.save()了,您就很好了
Parama Dharmika

是的,这需要保存上下文,否则将不会发生任何更改
Surendra Kumar

5

Swift 3.XSwift 4.X,简单的方法。仅更改 YourTable

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "YourTable")
    fetchRequest.returnsObjectsAsFaults = false

    do
    {
        let results = try context.fetch(fetchRequest)
        for managedObject in results
        {
            let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
            context.delete(managedObjectData)
        }
    } catch let error as NSError {
        print("Detele all my data in \(entity) error : \(error) \(error.userInfo)")
    }

您也可以使用这种构造:let fetchRequest:NSFetchRequest <NSFetchRequestResult> = YourTable.fetchRequest()
Daniil Chuiko

5

iOS 10及更高版本

适用于所有版本。传递实体名称并进行遍历以删除所有条目并保存上下文。

func deleteData(entityToFetch: String, completion: @escaping(_ returned: Bool) ->()) {
        let context = NSManagedObjectContext()
        context = your managedObjectContext

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
        fetchRequest.entity = NSEntityDescription.entity(forEntityName: entityToFetch, in: context)
        fetchRequest.includesPropertyValues = false
         do {   
            let results = try context.fetch(fetchRequest) as! [NSManagedObject]
            for result in results {
                context.delete(result)
            }
            try context.save()
            completion(true)
        } catch {
            completion(false)
            print("fetch error -\(error.localizedDescription)")
        }
    }

2
感谢您发布答案。这对我行得通。但是,您不应该只是将代码复制并粘贴到此处。对于newbee目前尚不清楚你什么CoreDataStack()DataController()类。更新将不胜感激;)
Nico S.

4

扩展Dave Delong的答案。

Swift版本,可同时处理iOS 9和以前的版本。我还介绍了以下错误处理:

让appDelegate:AppDelegate = UIApplication.sharedApplication()。delegate为!AppDelegate

    let fetchRequest = NSFetchRequest(entityName: "Car")
    if #available(iOS 9.0, *) {
        let delete = NSBatchDeleteRequest(fetchRequest: fetchRequest)
        do {
            try appDelegate.persistentStoreCoordinator.executeRequest(delete, withContext: appDelegate.managedObjectContext)
        } catch let error as NSError {
            print("Error occured while deleting: \(error)")
        }
    } else {
        // Fallback on earlier versions
        let carRequest = NSFetchRequest()
        carRequest.entity = NSEntityDescription.entityForName("Cars", inManagedObjectContext: appDelegate.managedObjectContext)
        carRequest.includesPropertyValues = false

        do {
            let cars: NSArray = try appDelegate.managedObjectContext.executeFetchRequest(carRequest)

            for car in cars {
                appDelegate.managedObjectContext.delete(car)
            }

            try appDelegate.managedObjectContext.save()

        } catch let error as NSError {
            print("Error occured while fetching or saving: \(error)")
        }
    }

赞成。ios 9删除记录的方法确实是awsm。
Shobhakar Tiwari

2

为什么不折叠现有缓存中收到的数据?否则,它并不是真正的“刷新”,而是“重新启动”,您还可以删除/删除SQLLite文件并重新启动(假设您也没有持久化其他数据)。


1
不好的解决方案。如果Sqlite数据库中还有其他表,显然我们将失去所有这些。对于特定的解决方案,这更是一种技巧,对于较大的情况则不予考虑。
Deepak GM

2

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(describing:self))


1

如果实体包含很多条目,则最好的方法是这样,因为它可以节省内存

 - (void)deleteAll:(NSManagedObjectContext *)managedObjectContext entityName:(NSString *)entityName
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [managedObjectContext setUndoManager:nil];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];
    [fetchRequest setIncludesPropertyValues:NO];
    [fetchRequest setFetchLimit:100]; // you can change this number if you want
    NSError *error;
    NSArray *items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    while ([items count] > 0) {
        @autoreleasepool {
            for (NSManagedObject *item in items) {
                [managedObjectContext deleteObject:item];
            }
            if (![managedObjectContext save:&error]) {
                NSLog(@"Error deleting %@ - error:%@",self.entityName, error);
            }
        }
        items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    }
}

1

在Swift 3.0中

 func deleteAllRecords() {
        //delete all data
        let context = appDelegate.persistentContainer.viewContext

        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "YourClassName")
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)

        do {
            try context.execute(deleteRequest)
            try context.save()
        } catch {
            print ("There was an error")
        }
    }

1

此代码适用于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()
    }

1

iOS 9.0及更高版本:

NSBatchDeleteRequest用于删除核心数据中的记录。它的工作速度非常快,从实体中删除所有记录所需的时间更少。它要求NSFetchRequest有论据。如果要删除实体中的所有记录,可以使用它,它对我有用。

let manageObject:NSManagedObjectContext = appDelegateObject.managedObjectContext

let fetchRequest = NSFetchRequest(entityName: EnityName”)

let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

let persistCor:NSPersistentStoreCoordinator = appDelegateObject.persistentObject
 do {
        try persistCor.executeRequest(deleteRequest, withContext: manageObject)
        try manageObject.save()
    } catch {
        print(error?.localizedDescription)
    }

1

快速清除数据库中的所有对象

func purgeAllData() {
    let uniqueNames = persistentContainer.managedObjectModel.entities.compactMap({ $0.name })

    uniqueNames.forEach { (name) in
      let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: name)
       let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
         do {
        try persistentContainer.viewContext.execute(batchDeleteRequest)
      } catch {
        let nserror = error as NSError
        fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
      }
   }
 }

0

Dave Delongs的Swift 2.0答案对我来说崩溃了(在iOS 9中)

但这有效:

let fetchRequest = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

    do {
        try managedObjectContext.executeRequest(deleteRequest)
        try managedObjectContext.save()
    }
    catch let error as NSError {
       // Handle error
    }

0

带有iOS 9'NSBatchDeleteRequest'的Swift 3解决方案,以及作为'NSManagedObjectContext'的扩展实现的后备iOS版本。Apple参考https://developer.apple.com/library/content/featuredarticles/CoreData_Batch_Guide/BatchDeletes/BatchDeletes.html

extension NSManagedObjectContext {
    func batchDeleteEntities<T: NSManagedObject>(ofType type: T.Type) throws {
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: type.self))
        if #available(iOS 9.0, *) {
            let request = NSBatchDeleteRequest(fetchRequest: fetchRequest)
            let result = try execute(request) as? NSBatchDeleteResult
            if let objectIDArray = result?.result as? [NSManagedObjectID] {
                let changes = [NSDeletedObjectsKey: objectIDArray]
                NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self])
            }
        } else {
            fetchRequest.includesPropertyValues = false
            let results = try fetch(fetchRequest)
            if let actualResults = results as? [NSManagedObject], !actualResults.isEmpty {
                actualResults.forEach { delete($0) }
            }
        }
    }
}

0

如果最低iOS为9.0,请使用NSBatchDeleteRequest删除多个记录。如果是后台线程,则执行NSManagedObjectContext保存,否则使用NSFetchRequest获取记录并删除for循环中的所有记录,并在删除完成后保存。


0

在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)")
        }

您必须在执行后调用reset。如果不是,它将不会在表视图上更新。


0
    func deleteAll(entityName: String) {

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
    let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
    deleteRequest.resultType = .resultTypeObjectIDs
    guard let context = self.container?.viewContext
        else { print("error in deleteAll")
            return }

    do {
        let result = try context.execute(deleteRequest) as? NSBatchDeleteResult
        let objectIDArray = result?.result as? [NSManagedObjectID]
        let changes: [AnyHashable : Any] = [NSDeletedObjectsKey : objectIDArray as Any]
        NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [context])
    } catch {
        print(error.localizedDescription)
    }
}

0

没有任何字符串作为实体名称的OOP方法 Swift 3 +,Xcode 10+

func batchDelete<T>(in context: NSManagedObjectContext, fetchRequest: NSFetchRequest<T>) throws {
    guard let request = fetchRequest as? NSFetchRequest<NSFetchRequestResult> else {
        throw ErrorService.defaultError
    }
    let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: request)
    do {
        try context.execute(batchDeleteRequest)
    } catch {
        throw error
    }
}

然后只需调用do / catch块

    let fetchRequest: NSFetchRequest<YourEntity> = YourEntity.fetchRequest()
    do {
        let data = try context.fetch(fetchRequest)
        if data.count > 0 {
            try self.batchDelete(in: context, fetchRequest: fetchRequest)
        }
    } catch {
        // throw error
    }

-1

在Swift 2.0中:

func deleteAllData(entity: String)
{
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext
    let fetchRequest = NSFetchRequest(entityName: entity)
    fetchRequest.returnsObjectsAsFaults = false

    do 
    {
        let results = try managedContext.executeFetchRequest(fetchRequest)
        for managedObject in results
        {
            let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
            managedContext.deleteObject(managedObjectData)
        }
    } catch let error as NSError {
        print("Detele all data in \(entity) error : \(error) \(error.userInfo)")
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.