I'm using Core Data to locally persist results from a Web Services call. The web service returns the full object model for, let's say, "Cars" - could be about 2000 of them (and I can't make the Web Service return anything less than 1 or ALL cars.
The next time I open my application, I want to refresh the Core Data persisted copy by calling the Web Service for all Cars again, however to prevent duplicates I would need to purge all data in the local cache first.
Is there a quicker way to purge ALL instances of a specific entity in the managed object context (e.g. all entities of type "CAR"), or do I need to query them call, then iterate through the results to delete each, then save?
Ideally I could just say delete all where entity is Blah.
转载于:https://stackoverflow.com/questions/1383598/core-data-quickest-way-to-delete-all-instances-of-an-entity
iOS 9 added a new class called NSBatchDeleteRequest
that allows you to easily delete objects matching a predicate without having to load them all in to memory. Here's how you'd use it:
let fetchRequest = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try myPersistentStoreCoordinator.executeRequest(deleteRequest, withContext: myContext)
} catch let error as NSError {
// TODO: handle the error
}
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Car"];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];
NSError *deleteError = nil;
[myPersistentStoreCoordinator executeRequest:delete withContext:myContext error:&deleteError];
More information about batch deletions can be found in the "What's New in Core Data" session from WWDC 2015 (starting at ~14:10).
Fetch 'em all and delete 'em all:
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
This is a similar question to the one here and someone suggested setting up a relationship delete rule so you only have to delete one object. So if you have or can make an entity with a to-many relationship to the cars and set the delete rule to cascade when you delete the higher entity all the cars will be deleted as well. This may save some processing time since you don't have to do the steps involved with loading ALL the cars. In a larger data set this could be absolutely necessary.
A little bit more cleaned and universal : Add this method :
- (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];
}
A good answer was already posted, this is only a recommendation!
A good way would be to just add a category to NSManagedObject
and implement a method like I did:
Header File (e.g. NSManagedObject+Ext.h
)
@interface NSManagedObject (Logic)
+ (void) deleteAllFromEntity:(NSString*) entityName;
@end
Code File: (e.g. 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
... the only thing you have to is to get the managedObjectContext from the app delegate, or where every you have it in ;)
afterwards you can use it like:
[NSManagedObject deleteAllFromEntity:@"EntityName"];
one further optimization could be that you remove the parameter for tha entityname and get the name instead from the clazzname. this would lead to the usage:
[ClazzName deleteAllFromEntity];
a more clean impl (as category to 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
The usage then:
[managedObjectContext deleteAllFromEntity:@"EntityName"];
Swift:
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)")
}
For 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")
}
}
Extending Dave Delong's answer.
Swift Version that takes care of iOS 9 and previous versions as well. I have also covered Error handling in this:
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! 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)")
}
}
Reset Entity in 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")
}
}